繁体   English   中英

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忽略默认参数并引发错误“否”可行的构造函数或演绎指南,用于推导......的模板参数。“

// 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'
}
  • Moving = void to forward声明修复了这个问题,但对我来说不可行,因为我的forward-decl在不同的文件中,我不知道哪一个会先出现。 (也是超级问题,因为我的默认值将在代码库深处的某个模糊文件中)
  • 改变Example e; Example<> e; 修复了这个问题,但对我来说不可行,因为我是一个库开发人员,并且不希望我的所有用户在我的课程后输入<>
  • 添加一个前向声明文件example_fwd.hpp ,其中包含一个前向声明并包含它,而不是每次都向前声明修复问题,但如果有更好的解决方案,我想避免这种情况。

在这种情况下谁是对的:clang或其他编译器? 这是编译器错误吗? 我怎样才能绕过这个问题(除了我上面描述的部分解决方案)? 我找到了#10147 (和相关的stackoverflow问题),但它是关于模板模板参数,并且在一年前也被标记为已修复。

编辑

这看起来像一个bug,现在报告在LLVM bugtracker#40488 )上。

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

我怎样才能绕过这个问题(除了我上面描述的部分解决方案)?

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

Example() -> Example<>;

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

template <class T>
class Example;

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

Example() -> Example<>;

int main() {
    Example e;
}

考虑以下因素:

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

 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的答案中提出 ,您可以提供使用默认参数的演绎指南

Example() -> Example<>;

该标准不区分定义中是否定义了默认模板参数或模板的声明。

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

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

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

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

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

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

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

作为示例,请参阅iosfwdlibstdc ++ / iosfwd

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM