简体   繁体   English

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

[英]Automatic constructor in explicitly instantiated class template

I have a template<bool VAR> struct Obj template declared in a header file ( obj.h ) with explicit automatic move constructor ( = default ).我有一个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>;

The member function of the template is defined in another file ( obj.cpp ) with explicit instantiation of the template:模板的成员函数在另一个文件( 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>;

This template is then used from the main file ( main.cpp ):然后从主文件 ( 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();
}

The .cpp s are then compiled and linked together with the following Makefile :然后.cpp被编译并与以下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 $@

However, I get a linker error: undefined reference to 'Obj<true>::Obj(Obj<true>&&)' -- the compiler apparently did not instantiate the constructor.但是,我收到一个链接器错误: undefined reference to 'Obj<true>::Obj(Obj<true>&&)' ——编译器显然没有实例化构造函数。

  • Obj<true>::member_fun() is defined and the program indeed links successfully if I remove the reference to the move constructor from main.cpp . Obj<true>::member_fun()已定义,如果我从main.cpp删除对移动构造函数的引用,则程序确实链接成功。
  • If I remove extern template from the header, the program compiles.如果我从头文件中删除extern template ,程序就会编译。
  • If I use int instead of std::vector<int> for the type of member , the program also compiles.如果我使用int而不是std::vector<int>作为member类型,程序也会编译。
  • cppreference.com claims that "the compiler will declare a move constructor as a non-explicit inline public member of its class". cppreference.com声称“编译器会将移动构造函数声明为其类的非显式内联公共成员”。 However, the manually defined Obj(int) constructor is also inline, but it is correctly instantiated.但是,手动定义的Obj(int)构造函数也是内联的,但它被正确实例化。

(I received this error with Clang in a project that compiled fine with GCC, so I thought this was a Clang bug. However, when I reduced the problem to this simple case, both GCC 5.4.0 and Clang 3.8.0 produce the same results.) (我在一个用 GCC 编译得很好的项目中收到了这个错误,所以我认为这是一个 Clang 错误。但是,当我将问题简化为这个简单的情况时,GCC 5.4.0 和 Clang 3.8.0 产生相同的结果。)

Interesting.有趣的。 I think your code is correct, because:我认为您的代码是正确的,因为:

Your defaulted move-constructor is implicitly inline because of:您默认的移动构造函数是隐式inline因为:

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

... A user-provided explicitly-defaulted function (ie, explicitly defaulted after its first declaration) is defined at the point where it is explicitly defaulted. ... 用户提供的显式默认函数(即,在其第一次声明后显式默认)在它被显式默认的点定义。

And [class.mfct]/1 :[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])一个成员函数可以在它的类定义中定义([dcl.fct.def]),在这种情况下它是一个内联成员函数([dcl.fct.spec])

And thus is exempt from explicit template instantiation according to [temp.explicit]/10 (emphasis mine):因此根据[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.除了内联函数和变量、从它们的初始值设定项或返回值 ([dcl.spec.auto]) 推导出类型的声明、文字类型的 const 变量、引用类型的变量和类模板特化,显式实例化声明具有以下效果抑制它们所引用的实体的隐式实例化。 [ 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. [注意:目的是作为显式实例化声明主题的内联函数在使用 odr ([basic.def.odr]) 时仍将被隐式实例化,以便可以考虑将主体内联,但没有内联函数的外联副本将在翻译单元中生成。 — end note ] — 尾注 ]

In fact, if you try any optimization mode other than -O0 , the problem disappears.事实上,如果您尝试除-O0之外的任何优化模式,问题都会消失。

-O0 is a special mode in which inlined functions are not inlined. -O0是一种特殊模式,其中内联函数不内联。 But it shouldn't matter, the compiler must in that case generate the inline defaulted move-constructor like it does with the other constructor.但没关系,在这种情况下,编译器必须像其他构造函数一样生成inline默认移动构造函数。

So to me this looks like a compiler bug.所以对我来说,这看起来像是一个编译器错误。 Also look at LLVM#22763 and GCC#60796 .另请查看LLVM#22763GCC#60796

I see at least 2 possible workarounds:我看到至少有 2 种可能的解决方法:

Solution 1解决方案1

Do not use extern template ... for now (compilation times will suffer but otherwise no big deal).暂时不要使用extern template ... (编译时间会受到影响,否则没什么大不了的)。

Solution 2解决方案2

Trick the compiler into generating the suppressed constructor in obj.cpp欺骗编译器在obj.cpp生成被抑制的构造obj.cpp

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

This one will only be used in -O0 mode.这个只会在-O0模式下使用。 In production code the inlined version will be used instead.在生产代码中,将使用内联版本。

There doesn't exist a lot of information about this topic on the internet.互联网上没有很多关于这个主题的信息。 Though looking at some sources, I would conclude the following:虽然查看了一些来源,但我得出以下结论:

extern template should prevent implicit instantiations, though in all examples, the explicit instantiation doesn't have this extern template definition. extern template应该防止隐式实例化,尽管在所有示例中, explicit实例化没有这个extern template定义。

From what I can read, especially on the proposal and the GCC mailing list (see references below), the extern template doesn't prevent implicit instantiations, though ALL instantiations of the template.从我所读到的,尤其是在提案和 GCC 邮件列表(参见下面的参考资料)中, extern template不会阻止implicit实例化,尽管模板的所有实例化。 This would include your explicit instantiation.这将包括您的explicit实例化。

If an entity is the subject of both an explicit instantiation declaration and an explicit instantiation definition in the same translation unit, the definition shall follow the declaration.如果实体是同一翻译单元中显式实例化声明和显式实例化定义的主题,则定义应遵循声明。 - John Spicer on GCC mailing list - GCC 邮件列表上的 John Spicer

From this, I would conclude that you should remove the extern template in the translation unit where you want the explicit instantiation.由此,我得出的结论是,您应该在需要显式实例化的翻译单元中删除extern template

References:参考:

It's possible that you are hitting a compiler bug.您可能遇到了编译器错误。

See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60796 .请参阅https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60796

I'm hitting similar behavior on CLang, but can't find a bug report.我在 CLang 上遇到了类似的行为,但找不到错误报告。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 显式实例化模板类的显式实例化模板方法 - Explicitly instantiate template method of explicitly instantiated template class 从 dll 导入显式实例化的模板 class - Importing explicitly instantiated template class from dll 如何在Visual Studio中导出从显式实例化模板派生的类? - How to export a class derived from an explicitly instantiated template in Visual Studio? 除非显式专用,否则未实例化模板类的静态成员? - Static member of template class not instantiated unless explicitly specialized? 在由另一个构造函数创建的模板 class 中实例化的未使用构造函数 - Unused constructor instantiated in template class created by another constructor C ++-在模板类中显式调用构造函数模板? - C++ - Invoking a constructor template explicitly inside a template class? 未定义的对显式实例化模板函数的引用 - Undefined reference to explicitly instantiated template function 显式实例化模板方法中的编译错误 - Compilation error in explicitly instantiated template methods 将显式实例化的函数模板与转换匹配 - Matching explicitly instantiated function template with conversion 如何在没有警告的情况下使用从 dll 中显式实例化的模板派生的 dllexport-ed 类? - How to use a dllexport-ed class which is derived from an explicitly instantiated template in a dll without warnings?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM