简体   繁体   English

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

[英]Clang does not notice default template parameters

Background 背景

According to the C++ standard, when forward-declaring a template type with default template parameters, each of them can appear in one declaration only. 根据C ++标准,当使用默认模板参数向前声明模板类型时,它们中的每一个都只能出现在一个声明中。 For example: 例如:

// 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

Problem 问题

In my code I have a lot of forward declaration in different files, so it makes sense to put my default parameters in the definition: 在我的代码中,我在不同的文件中有很多前向声明,所以将默认参数放在定义中是有意义的:

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

and, as expected, it all works well everywhere... except clang! 而且,正如所料,它在所有地方都运作良好......除了铿锵! I narrowed the problem down to this: 我将问题缩小到这个范围:
In clang, if the class template has default parameters, but they are not declared in the first forward declaration of that class, and when declaring an instance of that class no angle brackets are specified , clang ignores the default parameter and raises an error "no viable constructor or deduction guide for deduction of template arguments of ...". 在clang中,如果类模板具有默认参数,但它们未在该类的第一个前向声明中声明 ,并且在声明该类的实例时未指定尖括号 ,则clang忽略默认参数并引发错误“否”可行的构造函数或演绎指南,用于推导......的模板参数。“

Example

// 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 the forward declaration fixes the issue, but is not viable for me since my forward-decls are in different files and I don't know which one will appear first. Moving = void to forward声明修复了这个问题,但对我来说不可行,因为我的forward-decl在不同的文件中,我不知道哪一个会先出现。 (Also super problematic since my defaults would be in some obscure file deep in the codebase) (也是超级问题,因为我的默认值将在代码库深处的某个模糊文件中)
  • Changing Example e; 改变Example e; to Example<> e; Example<> e; fixes the issue, but is not viable for me since I'm a library dev and don't want all my users typing <> after my classes. 修复了这个问题,但对我来说不可行,因为我是一个库开发人员,并且不希望我的所有用户在我的课程后输入<>
  • Adding a forward-declaration file example_fwd.hpp with one forward-declaration and including it instead of forward declaring every time fixes the issue, but I would like to avoid this if there is a better solution. 添加一个前向声明文件example_fwd.hpp ,其中包含一个前向声明并包含它,而不是每次都向前声明修复问题,但如果有更好的解决方案,我想避免这种情况。

Question

Who is right in this case: clang or the other compilers? 在这种情况下谁是对的:clang或其他编译器? Is this a compiler bug? 这是编译器错误吗? How can I circumvent this issue (apart from partial solutions I described above)? 我怎样才能绕过这个问题(除了我上面描述的部分解决方案)? I've found #10147 (and related stackoverflow questions), but it's about template template params and also is marked as fixed over a year ago. 我找到了#10147 (和相关的stackoverflow问题),但它是关于模板模板参数,并且在一年前也被标记为已修复。

Edit 编辑

This looks like a bug, and is now reported on LLVM bugtracker ( #40488 ). 这看起来像一个bug,现在报告在LLVM bugtracker#40488 )上。

I don't know who's right but... 我不知道谁是对的,但......

How can I circumvent this issue (apart from partial solutions I described above)? 我怎样才能绕过这个问题(除了我上面描述的部分解决方案)?

What about adding the following deduction rule? 添加以下扣除规则怎么样?

Example() -> Example<>;

The following code compile (C++17, obviously) with both g++ and clang++ 以下代码使用g ++和clang ++编译(显然是C ++ 17)

template <class T>
class Example;

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

Example() -> Example<>;

int main() {
    Example e;
}

Considering the following: 考虑以下因素:

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

 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 ] - 结束例子 ]

The default arguments available for 可用的默认参数

template <class T>
class Example;

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

will be the defaults arguments in the definition of Example . 将是Example定义中的默认参数。 The two declarations above will be equivalent to have a single declaration as 上面的两个声明将相当于具有单个声明

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

which will effectively allow doing Example e . 这将有效地允许做Example e

The original code should be accepted. 应接受原始代码。 As a workaround and already suggested in max66's answer , you can provide a deduction guide that uses the default argument 作为一种解决方法,已在max66的答案中提出 ,您可以提供使用默认参数的演绎指南

Example() -> Example<>;

The standard does not make distinction whether a default template argument is defined in the definition or a declaration of a template. 该标准不区分定义中是否定义了默认模板参数或模板的声明。

Because Clang accepts the code when the default argument appears in the declaration, but not in the definition, at least one of this two behaviors is wrong. 因为Clang在声明中出现默认参数时接受代码,但在定义中没有,所以这两种行为中至少有一种是错误的。 Considering [over.match.class.deduct]/1.1.1: 考虑[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. 模板参数是C的模板参数,后跟构造函数的模板参数(包括默认模板参数),如果有的话。

, I am tempted to say that Clang should use the default template argument. ,我很想说Clang应该使用默认的模板参数。

I think that you could avoid this bug by following a common practice: 我认为您可以通过遵循常规做法来避免此错误:

  1. If a declaration must be forwarded, create a dedicated header file for this forward declaration. 如果必须转发声明,请为此前向声明创建专用头文件。

  2. Define default arguments in this forward declaration file 在此前向声明文件中定义默认参数

  3. Also include this file in the header file that provides the definition of the template. 还要在头文件中包含此文件,该文件提供模板的定义。

As an example see iosfwd : libstdc++/iosfwd 作为示例,请参阅iosfwdlibstdc ++ / iosfwd

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

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