显式实例化的类模板中的自动构造函数 [英] Automatic constructor in explicitly instantiated class template

查看:132
本文介绍了显式实例化的类模板中的自动构造函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a template<bool VAR> struct Obj template declared in a header file (obj.h) with explicit automatic move constructor (= 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 ),其中包含模板的显式实例:

The member function of the template is defined in another file (obj.cpp) with explicit instantiation of the template:

// 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 ):

This template is then used from the main file (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 链接在一起:

The .cpps are then compiled and linked together with the following 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 $@

但是,出现链接器错误:未定义的引用'Obj< true> :: Obj(Obj< true&&))'-显然编译器没有实例化构造函数。

However, I get a linker error: undefined reference to 'Obj<true>::Obj(Obj<true>&&)' -- the compiler apparently did not instantiate the constructor.


  • Obj< true> :: member_fun()已定义且程序确实如果我从 main.cpp 删除对move构造函数的引用,则成功链接。

  • 如果我删除 extern模板从头开始,程序将进行编译。

  • 如果我使用 int 而不是 std :: vector< int< int> 表示成员的类型,程序也会编译。

  • cppreference.com 声称编译器会将移动构造函数声明为非-显式 inline 该类的公共成员。但是,手动定义的 Obj(int)构造函数也是内联的,但已正确实例化。

  • Obj<true>::member_fun() is defined and the program indeed links successfully if I remove the reference to the move constructor from main.cpp.
  • If I remove extern template from the header, the program compiles.
  • If I use int instead of std::vector<int> for the type of member, the program also compiles.
  • cppreference.com claims that "the compiler will declare a move constructor as a non-explicit inline public member of its class". However, the manually defined Obj(int) constructor is also inline, but it is correctly instantiated.

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

(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.)

推荐答案

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

Interesting. I think your code is correct, because:

您的默认移动构造函数隐式地 inline ,原因是:

Your defaulted move-constructor is implicitly inline because of:

[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.

a href = http://eel.is/c++draft/class.mfct#1 rel = nofollow> [class.mfct] / 1 :

And [class.mfct]/1:


成员函数可以在其类
定义中定义([dcl.fct.def]),在这种情况下,它是一个内联成员函数
([dcl.fct.spec])

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 (重点是我的):

And thus is exempt from explicit template instantiation according to [temp.explicit]/10 (emphasis mine):


除内联函数和变量外,从其初始值设定项或返回值([dcl.spec.auto]推导出的类型为
的声明) ),文字类型的
const变量,引用类型的变量和
类模板特化,显式实例化声明
具有抑制
实体隐式实例化的作用。他们参考。 [注意:意图是作为显式实例化声明
的主题的内联
函数在odr-use([basic.def.odr])
时仍将隐式实例化,因此可以考虑内联,但是在
转换单元中不会生成
内联函数的脱机副本。 —尾注]

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 ,问题消失了。

In fact, if you try any optimization mode other than -O0, the problem disappears.

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

-O0 is a special mode in which inlined functions are not inlined. But it shouldn't matter, the compiler must in that case generate the inline defaulted move-constructor like it does with the other constructor.

对我来说,这看起来像是编译器错误。还要查看 LLVM#22763 GCC#60796

So to me this looks like a compiler bug. Also look at LLVM#22763 and GCC#60796.

我认为至少有2种可能解决方法:

I see at least 2 possible workarounds:

解决方案1 ​​

请勿使用 extern模板... 目前(编译时间会受到影响,但除此之外没什么大不了的。)

Do not use extern template ... for now (compilation times will suffer but otherwise no big deal).

解决方案2

欺骗编译器在 obj.cpp

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

该选项仅用于 -O0 模式。在生产代码中,将使用内联版本。

This one will only be used in -O0 mode. In production code the inlined version will be used instead.

这篇关于显式实例化的类模板中的自动构造函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆