Clang 不会注意到默认的模板参数
Clang does not notice default template parameters
背景
根据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 中,如果 class 模板有默认参数,但它们没有在那个 class 的 的第一个前向声明 中声明,并且在声明那个的实例时class 未指定尖括号,clang 忽略默认参数并引发错误 "no viable constructor or deduction guide for deduction of template arguments of ..."。
例子
// 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;
解决了这个问题,但对我来说不可行,因为我是图书馆开发人员并且不希望我的所有用户在我之后输入 <>
classes.
- 添加一个前向声明文件
example_fwd.hpp
并包含一个前向声明,而不是每次都前向声明解决了这个问题,但如果有更好的解决方案,我想避免这种情况。
问题
在这种情况下谁是对的:clang 还是其他编译器?这是编译器错误吗?我怎样才能避免这个问题(除了我上面描述的部分解决方案)?
我找到了 #10147(以及相关的 Whosebug 问题),但它是关于模板模板参数的,并且在一年前也被标记为已修复。
编辑
这看起来像是一个错误,现在已在 LLVM 错误追踪器 (#40488) 上报告。
我不知道谁是对的但是...
How can I circumvent this issue (apart from partial solutions I described above)?
添加以下扣除规则怎么样?
Example() -> Example<>;
以下代码使用 g++ 和 clang++ 编译(显然是 C++17)
template <class T>
class Example;
template <class T = void>
class Example {};
Example() -> Example<>;
int main() {
Example e;
}
标准不区分默认模板参数是在模板的定义中定义还是在模板声明中定义。
因为当默认参数出现在声明中而不是定义中时,Clang 接受代码,所以这两种行为中至少有一种是错误的。考虑到 [over.match.class.deduct]/1.1.1:
The template parameters are the template parameters of C followed by the template parameters (including default template arguments) of the constructor, if any.
,我很想说Clang应该使用默认的模板参数。
我认为您可以通过以下常见做法避免此错误:
如果必须转发声明,请为该转发声明创建专用头文件。
在此前向声明文件中定义默认参数
还将此文件包含在提供模板定义的头文件中。
示例参见 iosfwd
:libstdc++/iosfwd
考虑以下因素:
[temp.param]/12 - The set of default template-arguments available for use is obtained by merging the default arguments from all prior declarations of the template in the same way default function arguments are [ Example:
template<class T1, class T2 = int> class A;
template<class T1 = int, class T2> class A;
is equivalent to
template<class T1 = int, class T2 = int> class A;
— end example ]
可用于
的默认参数
template <class T>
class Example;
template <class T = void>
class Example {};
将是 Example
定义中的默认参数。上面的两个声明相当于有一个声明
template <class T = void>
class Example {};
这将有效地允许 Example e
。
应接受原始代码。作为解决方法并且已在 中建议,您可以提供使用默认参数的推导指南
Example() -> Example<>;
背景
根据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 中,如果 class 模板有默认参数,但它们没有在那个 class 的 的第一个前向声明 中声明,并且在声明那个的实例时class 未指定尖括号,clang 忽略默认参数并引发错误 "no viable constructor or deduction guide for deduction of template arguments of ..."。
例子
// 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;
解决了这个问题,但对我来说不可行,因为我是图书馆开发人员并且不希望我的所有用户在我之后输入<>
classes. - 添加一个前向声明文件
example_fwd.hpp
并包含一个前向声明,而不是每次都前向声明解决了这个问题,但如果有更好的解决方案,我想避免这种情况。
问题
在这种情况下谁是对的:clang 还是其他编译器?这是编译器错误吗?我怎样才能避免这个问题(除了我上面描述的部分解决方案)? 我找到了 #10147(以及相关的 Whosebug 问题),但它是关于模板模板参数的,并且在一年前也被标记为已修复。
编辑
这看起来像是一个错误,现在已在 LLVM 错误追踪器 (#40488) 上报告。
我不知道谁是对的但是...
How can I circumvent this issue (apart from partial solutions I described above)?
添加以下扣除规则怎么样?
Example() -> Example<>;
以下代码使用 g++ 和 clang++ 编译(显然是 C++17)
template <class T>
class Example;
template <class T = void>
class Example {};
Example() -> Example<>;
int main() {
Example e;
}
标准不区分默认模板参数是在模板的定义中定义还是在模板声明中定义。
因为当默认参数出现在声明中而不是定义中时,Clang 接受代码,所以这两种行为中至少有一种是错误的。考虑到 [over.match.class.deduct]/1.1.1:
The template parameters are the template parameters of C followed by the template parameters (including default template arguments) of the constructor, if any.
,我很想说Clang应该使用默认的模板参数。
我认为您可以通过以下常见做法避免此错误:
如果必须转发声明,请为该转发声明创建专用头文件。
在此前向声明文件中定义默认参数
还将此文件包含在提供模板定义的头文件中。
示例参见 iosfwd
:libstdc++/iosfwd
考虑以下因素:
[temp.param]/12 - The set of default template-arguments available for use is obtained by merging the default arguments from all prior declarations of the template in the same way default function arguments are [ Example:
template<class T1, class T2 = int> class A; template<class T1 = int, class T2> class A;
is equivalent to
template<class T1 = int, class T2 = int> class A;
— end example ]
可用于
的默认参数template <class T>
class Example;
template <class T = void>
class Example {};
将是 Example
定义中的默认参数。上面的两个声明相当于有一个声明
template <class T = void>
class Example {};
这将有效地允许 Example e
。
应接受原始代码。作为解决方法并且已在
Example() -> Example<>;