背景
根据C++标准,当前向声明具有默认模板参数的模板类型时,每个模板类型只能出现在一个声明中。例如:
// 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 {};而且,就像预期的那样,一切都很顺利.除了嘎吱声!我把问题缩小到以下几个方面:
在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 (以及相关的堆栈溢出问题),但是它是关于模板params的,并且在一年前被标记为固定的。
编辑
这看起来像一个bug,现在在LLVM (#40488)上有报道。
发布于 2019-01-24 18:58:33
考虑到以下各点:
[temp.param]/12 --一组可用的默认模板参数是通过合并模板先前所有声明中的默认参数获得的,就像默认函数参数是 示例一样: template类A;模板类A; 等于 模板A类; - end实例
的默认参数。
template <class T>
class Example;
template <class T = void>
class Example {};将是Example定义中的默认参数。上面的两个声明将等于有一个声明为
template <class T = void>
class Example {};这将有效地允许执行Example e。
原代码应被接受。作为一种解决方法,并且已经在max66 66的答案中建议,您可以提供使用默认参数的演绎指南。
Example() -> Example<>;发布于 2019-01-24 17:53:24
我不知道谁是对的但是..。
我怎样才能绕过这个问题(除了我上面描述的部分解决方案)?
添加以下扣减规则如何?
Example() -> Example<>;下面的代码编译(显然是C++17)同时使用g++和clang++
template <class T>
class Example;
template <class T = void>
class Example {};
Example() -> Example<>;
int main() {
Example e;
}发布于 2019-01-24 18:52:50
标准不区分默认模板参数是在定义中定义的,还是在模板的声明中定义的。
因为当默认参数出现在声明中,而不是在定义中时,Clang接受代码,因此这两种行为中至少有一种是错误的。考虑[over.match.class.deduct]/1.1.1:
模板参数是C的模板参数,然后是构造函数的模板参数(包括默认模板参数)(如果有的话)。
,我想说Clang应该使用默认的模板参数。
我认为您可以通过遵循一个常见的实践来避免这个错误:
作为示例,请参阅iosfwd:libstdc++/iosfwd
https://stackoverflow.com/questions/54351543
复制相似问题