【发布时间】:2016-08-13 06:35:06
【问题描述】:
我在带有显式自动移动构造函数 (= default) 的头文件 (obj.h) 中声明了一个 template<bool VAR> struct Obj 模板。
// 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();
}
然后将.cpps 编译并与以下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 声称“编译器会将移动构造函数声明为其类的非显式 inline 公共成员”。但是,手动定义的
Obj(int)构造函数也是内联的,但它被正确实例化了。
(我在一个使用 GCC 编译良好的项目中收到了 Clang 的此错误,所以我认为这是一个 Clang 错误。但是,当我将问题简化为这种简单的情况时,GCC 5.4.0 和 Clang 3.8.0产生相同的结果。)
【问题讨论】:
-
原来用GCC编译的项目是用C++11还是C++98?
-
它使用 C++14 (
-std=c++14)。 -
我猜这与显式实例化和内联有关。有一个 vector
可能会使默认的构造函数非内联。 -
@skypjack:没有必要在同一个文件中定义模板,只要模板的每次使用都在程序的某个目标文件中实例化,我在@中通过显式实例化来完成987654343@文件。
-
@rustyx 是的,可能有一些模板、
inline和自动定义的交互。然而,cppreference.com (en.cppreference.com/w/cpp/language/move_constructor) 说“......编译器将声明一个移动构造函数作为其类的非显式 inline 公共成员......”。请注意,我手动定义的另一个构造函数Obj(int)也是inline,但它是在main.cpp中定义的。