c++ - 显式实例化类模板中的自动构造函数

标签 c++ templates c++11

我有一个 template<bool VAR> struct Obj在头文件 ( obj.h ) 中使用显式自动移动构造函数 ( = default ) 声明的模板。

// obj.h
#pragma once
#include <vector>

template<bool VAR>
struct Obj {
  std::vector<int> member;
  Obj(int m): member(m) { }
  Obj(Obj&&) = default;
  int member_fun() const;
};

extern template struct Obj<false>;
extern template struct Obj<true>;

模板的成员函数在另一个文件(obj.cpp)中定义,并显式实例化模板:

// obj.cpp
#include "obj.h"

template<bool VAR>
int Obj<VAR>::member_fun() const {
  return 42;
}

template struct Obj<false>;
template struct Obj<true>;

然后从主文件 ( main.cpp ) 使用此模板:

// main.cpp
#include <utility>
#include "obj.h"

int main() {
  Obj<true> o1(20);
  Obj<true> o2(std::move(o1));
  return o2.member_fun();
}

.cpp然后将 s 编译并与以下 Makefile 链接在一起:

#CXX=clang++
CXX=g++
CXXFLAGS=-Wall -Wextra -std=c++14

a.out: obj.o main.o
    $(CXX) $(CXXFLAGS) $^ -o a.out

obj.o: obj.cpp obj.h
    $(CXX) $(CXXFLAGS) -c $< -o $@
main.o: main.cpp obj.h
    $(CXX) $(CXXFLAGS) -c $< -o $@

但是,我收到链接器错误:undefined reference to 'Obj<true>::Obj(Obj<true>&&)' -- 编译器显然没有实例化构造函数。

  • Obj<true>::member_fun()已定义,如果我从 main.cpp 中删除对移动构造函数的引用,程序确实会成功链接.
  • 如果我删除 extern template从头开始,程序编译。
  • 如果我使用 int而不是 std::vector<int>对于 member 的类型,程序也会编译。
  • cppreference.com声称“编译器会将移动构造函数声明为其类的非显式内联公共(public)成员”。但是,手动定义的 Obj(int)构造函数也是内联的,但已正确实例化。

(我在一个使用 GCC 编译良好的项目中收到了 Clang 的这个错误,所以我认为这是一个 Clang 错误。但是,当我将问题简化为这个简单的情况时,GCC 5.4.0 和 Clang 3.8.0产生相同的结果。)

最佳答案

很有趣。我认为您的代码是正确的,因为:

您的默认移动构造函数是隐式内联,因为:

[dcl.fct.def.default]/5 :

... A user-provided explicitly-defaulted function (i.e., explicitly defaulted after its first declaration) is defined at the point where it is explicitly defaulted.

[class.mfct]/1 :

A member function may be defined ([dcl.fct.def]) in its class definition, in which case it is an inline member function ([dcl.fct.spec])

因此根据 [temp.explicit]/10 免于显式模板实例化(强调我的):

Except for inline functions and variables, declarations with types deduced from their initializer or return value ([dcl.spec.auto]), const variables of literal types, variables of reference types, and class template specializations, explicit instantiation declarations have the effect of suppressing the implicit instantiation of the entity to which they refer. [ Note: The intent is that an inline function that is the subject of an explicit instantiation declaration will still be implicitly instantiated when odr-used ([basic.def.odr]) so that the body can be considered for inlining, but that no out-of-line copy of the inline function would be generated in the translation unit. — end note ]

事实上,如果您尝试除-O0之外的任何优化模式,问题就会消失。

-O0是内联函数不内联的特殊模式。但这无关紧要,在这种情况下,编译器必须生成 inline 默认移动构造函数,就像生成其他构造函数一样。

所以对我来说这看起来像是一个编译器错误。另请参阅 LLVM#22763GCC#60796 .

我看到至少 2 种可能的解决方法:

解决方案一

暂时不要使用 extern template ...(编译时间会受到影响,但其他没什么大不了的)。

方案二

诱使编译器在 obj.cpp 中生成被抑制的构造函数

template<> Obj<true>::Obj(Obj&&) noexcept = default;

这个只会在-O0 模式下使用。在生产代码中,将改用内联版本。

关于c++ - 显式实例化类模板中的自动构造函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39824682/

相关文章:

c++ - 获取 ostream 的插入运算符

c++ - 在 C++ 中增量构建编译时间列表

c++ - Variadic 模板元编程 : a bug in clang++ or g++?

c++ - 'const decltype((a))' 没有声明 const 引用?

C++消息查找表(iostream)

C++ 匿名结构

c++ - 在 C++ 中连接数组中的数据

c++ - 如何管理SVG文件中的超出范围的值?

c++ - 宏 htonl 将内部逗号解释为参数分隔符

c++ - 调用了错误的构造函数