在 GCC 中工作的外联构造函数模板在 Clang 中失败

out-of-line constructor template works in GCC fails in Clang

以下代码在 GCC 中运行良好,但在 clang 中无法编译:

#include <iostream>
#include <string>

template <typename T>
struct A {
template <typename C>
  A(const C& c) { std::cout << "base" << std::endl; }
};

template <>
template <>
A<std::string>::A<std::string>(const std::string& s) { 
  std::cout << s << std::endl; 
}

int main()
{
  std::string f("foo");
  A<std::string> a(f);
  A<std::string> b(1.2);
}

GCC 输出:

foo
base

但是 Clang 给出了以下错误:

source_file.cpp:14:17: error: out-of-line constructor for 'A' cannot have 
template arguments
A<std::string>::A<std::string>(const std::string& s) {              
                ^~~~~~~~~~~~~~
1 error generated.

我只是想确认是否有人知道 clang 是否 正确 不允许这样做,或者它是否是 clang 中的错误。如果我删除显式模板参数,则 clang 可以很好地推导模板参数、编译并生成正确的输出。只有显式模板参数才能编译失败,我想不出标准中有什么不允许这样做的。

谢谢

Clang 是正确的。

引自[temp.mem]/5:

[ Note: Because the explicit template argument list follows the function template name, and because conversion member function templates and constructor member function templates are called without using a function name, there is no way to provide an explicit template argument list for these function templates. — end note ]

正如您已经注意到的,您不需要在构造函数模板特化中重复模板参数,因为它是推导的。 A<std::string>::A 应该够了。

template <>
template <>
A<std::string>::A(const std::string& s) { 
  std::cout << s << std::endl; 
}

主要问题是构造函数实际上没有名字。仍然存在一个开放的 C++ 标准问题 581. Can a templated constructor be explicitly instantiated or specialized?,其中包含 2006 年的以下注释:

It was observed that explicitly specifying the template arguments in a constructor declaration is never actually necessary because the arguments are, by definition, all deducible and can thus be omitted.

Clang 评论提到 [class.qual]p2[8][9] and issue 1435[4] 作为抱怨和拒绝模板参数的参数。

6.4.3.1 Class members [class.qual]

[...]

2 In a lookup in which function names are not ignored and the nested-name-specifier nominates a class C:

— (2.1) if the name specified after the nested-name-specfier, when looked up in C, is the injected-class-name of C (Clause 12), or

[...]

the name is instead considered to name the constructor of class C.

唯一提到转换成员函数模板和构造成员函数模板不能与显式模板参数一起使用的地方是一个非规范性注释。

17.6.2 Member templates [temp.mem]

[...]

5 A specialization of a conversion function template is referenced in the same way as a non-template conversion function that converts to the same type. [...]

[Note: Because the explicit template argument list follows the function template name, and because conversion member function templates and constructor member function templates are called without using a function name, there is no way to provide an explicit template argument list for these function templates. —end note]