Clang没有注意到默认的模板参数

时间:2019-01-24 16:44:22

标签: c++ templates clang default-arguments

背景

根据C ++标准,当使用默认模板参数向前声明模板类型时,每个模板类型只能出现在 one 声明中。例如:

// GOOD example
template <class T = void>
class Example;    // forward-declaration

template <class T>
class Example {}; // definition
// GOOD example
template <class T>
class Example;    // forward-declaration

template <class T = void>
class Example {}; // definition
// BAD example
template <class T = void>
class Example;    // forward-declaration

template <class T = void> // ERROR: template parameter redefines default argument
class Example {}; // definition

问题

在我的代码中,我在不同的文件中有很多前向声明,因此将默认参数放入定义中很有意义:

// foo.hpp, bar.hpp, baz.hpp, etc.
template <class T>
class Example;
// example.hpp
template <class T = void>
class Example {};

,而且,正如预期的那样,它在任何地方都可以正常工作……除了c!我将问题缩小为:
在Clang中,如果类模板具有默认参数,但未在该类的第一个前向声明中声明,并且在声明该类的实例时,未指定尖括号 ,clang会忽略默认参数,并引发错误“没有可行的构造函数或推论指南来推导...的模板参数”。

示例

// GOOD example
template <class T>
class Example;

template <class T = void>
class Example {};

int main() {
    Example e; // error: no viable constructor or deduction guide for deduction of template arguments of 'Example'
}
  • = void移动到前向声明可以解决此问题,但对我而言不可行,因为我的前向决定位于不同的文件中,并且我不知道哪个会先出现。 (而且超级麻烦,因为我的默认设置位于代码库中一些晦涩的文件中)
  • Example e;更改为Example<> e;可以解决此问题,但对我而言不可行,因为我是图书馆开发人员,并且不希望我的所有用户在课后都输入<>
  • 添加带有一个前向声明的前向声明文件example_fwd.hpp并包含它而不是每次都进行前向声明都可以解决此问题,但是如果有更好的解决方案,我想避免这种情况。

问题

在这种情况下谁是正确的:clang或其他编译器?这是编译器错误吗?我该如何避免这个问题(除了上述的部分解决方案)? 我发现了#10147(以及相关的stackoverflow问题),但这是关于模板模板参数的,并且在一年前被标记为固定。

编辑

这看起来像是个错误,现在已在LLVM Bugtracker(#40488)中报告。

3 个答案:

答案 0 :(得分:5)

我不知道谁是对的,但是...

  

如何解决这个问题(除了上面介绍的部分解决方案)?

添加以下扣除规则怎么办?

Example() -> Example<>;

以下代码同时使用g ++和clang ++编译(显然是C ++ 17)

template <class T>
class Example;

template <class T = void>
class Example {};

Example() -> Example<>;

int main() {
    Example e;
}

答案 1 :(得分:3)

考虑以下几点:

  

[temp.param]/12 -可以使用的默认模板参数集是通过合并模板的所有先前声明中的默认参数而获得的,其方式与默认函数参数为[< em>示例:

template<class T1, class T2 = int> class A;
template<class T1 = int, class T2> class A;
     

等同于

template<class T1 = int, class T2 = int> class A;
     

-举个例子]

可用于

的默认参数
template <class T>
class Example;

template <class T = void>
class Example {};

将是Example定义中的默认参数。上面的两个声明等同于一个单独的声明

template <class T = void>
class Example {};

这将有效地允许执行Example e

原始代码应被接受。作为一种解决方法,max66's answer中已经建议过,您可以提供使用默认参数的推导指南

Example() -> Example<>;

答案 2 :(得分:0)

该标准没有区分是在模板的定义中还是在模板的声明中定义了默认模板参数。

因为当默认参数出现在声明中但未出现在定义中时,Clang接受代码,所以这两种行为中至少有一种是错误的。考虑[over.match.class.deduct]/1.1.1:

  

模板参数是C的模板参数,后跟构造函数的模板参数(包括默认模板参数)(如果有)。

,我很想说Clang应该使用默认的模板参数。

我认为您可以通过遵循常规做法来避免此错误:

  1. 如果必须转发声明,请为此转发声明创建专用的头文件。

  2. 在此前向声明文件中定义默认参数

  3. 还将此文件包含在提供模板定义的头文件中。

例如,请参见iosfwdlibstdc++/iosfwd