【问题标题】:Template aliases conflicting types. g++ compiles successfully while clang fails模板别名冲突类型。 g++ 编译成功,而 clang 失败
【发布时间】:2019-04-28 08:08:33
【问题描述】:

我遇到了一个非常奇怪的编译器错误。由于某种原因,发布的代码确实可以使用 g++ (7.3.0) 正确编译,而 clang (7.0.0) 失败:

../TemplateAlias/main.cpp:64:9: error: no matching function for call to 'freeFunc'
        freeFunc(new Func, dummyField);
        ^~~~~~~~
../TemplateAlias/main.cpp:73:12: note: in instantiation of member function 'Helper<Traits<double, ConcreteData, ConcreteField> >::func' requested here
    helper.func();
           ^
../TemplateAlias/main.cpp:21:13: note: candidate template ignored: deduced conflicting templates for parameter '' ('FieldData' vs. 'ConcreteData')
static void freeFunc(SomeFunc<T, FieldData>* func,
            ^

两个编译器选项都设置为 -std=c++14

template<typename T>
struct ConcreteData
{
    T data;
};

template<typename T, template<typename U> class FieldData>
struct ConcreteField
{
    FieldData<T> someMember;
};

template<typename T, template<typename U> class FieldData>
struct SomeFunc
{

};


template<typename T, template<typename U> class FieldData>
static void freeFunc(SomeFunc<T, FieldData>* func,
                     ConcreteField<T, FieldData>& field)
{
    // apply the func on data
    (void)field; // silence compiler warning
    delete func;
}


template<
        typename ScalarType,
        template<typename U> class FieldDataType,
        template<typename U, template <typename X> class Data> class FieldType
        >
struct Traits
{
    using Scalar = ScalarType;

    template<typename T>
    using FieldData = FieldDataType<T>;

    using Field = FieldType<Scalar, FieldDataType>; // fails with clang only
    // using Field = FieldType<Scalar, FieldData>; // using this line helps clang

};

template<typename Traits>
struct Helper
{
    // alias all types given by trait for easier access
    using Scalar = typename Traits::Scalar;
    using Field = typename Traits::Field;

    template<typename U>
    using DataAlias = typename Traits::template FieldData<U>;

    void func()
    {
         // using Func = SomeFunc<Scalar, DataAlias>; // this line is intended, but fails with both GCC and clang
         using Func = SomeFunc<Scalar, Traits::template FieldData>; // compiles only with GCC, fails with clang


        Field dummyField;
        freeFunc(new Func, dummyField);
    }
};


int main()
{
    using ConcreteTraits = Traits<double, ConcreteData, ConcreteField>;
    Helper<ConcreteTraits> helper;
    helper.func();

    return 0;
}

根据 cppreference.com:

类型别名声明引入了一个可以用作 type-id 表示的类型的同义词。它没有引入新的 type 并且它不能更改现有类型名称的含义。那里 类型别名声明和 typedef 没有区别 宣言。此声明可能出现在块范围、类范围、 或命名空间范围。

别名模板永远不会通过模板参数推导来推导 推导出一个模板模板参数。

据我了解,这两种类型(ConcreteData 和 FieldData)应该是等效的。为什么在这种情况下clang会失败,为什么在使用“第二阶段”别名时两个编译器都会失败?根据 C++ 标准,哪个编译器是正确的?是编译器错误还是对 C++14 标准的微妙含糊解释?

【问题讨论】:

  • 这是一个更精简的 MCVE:godbolt.org/z/nSFQ3n
  • “类型别名声明引入了一个名称...”指的是非模板化的using。更相关的 cppref 引用是“别名模板是一个模板,当专门化时,相当于用别名模板的模板参数替换 类型中的模板参数的结果- id”。 (由我加粗。)

标签: c++ templates c++14 typetraits template-aliases


【解决方案1】:

借用@Oktalist 的最小示例。

template <typename>
class T {};

template <typename _>
using U = T<_>;

template <template <typename> class X>
void f(A<X>, A<X>) {}

如果您将f 替换为:

template <template <typename> class X, template <typename> class Y>
void f(A<X>, A<Y>) {}

代码不再无法编译。可以看到问题出在模板参数XY的等价问题上,推导出来的类型不同。

别名模板生成的类型的等价性仅在引用别名的特殊化时才考虑,正如[temp.alias]/2 中所指定的那样:

当一个template-id 指代一个别名模板的特化时,它等价于用它的template-arguments代替type-id中的template-parameters得到的关联类型别名模板

使用此规则和等价规则[temp.type]/1

T&lt;int&gt;U&lt;int&gt; 是等价的,X&lt;T&lt;int&gt;&gt;Z&lt;U&lt;int&gt;&gt; 也是等价的,但是这条规则并没有扩展到别名模板 U 等价于类模板 T(他们自己,它们不是专精)。

别名FieldData 和类模板ConcreteData 的情况相同。

实际上有两个缺陷报告,CWG-1286CWG-1244 提出了别名模板的等效扩展。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-28
    • 2015-03-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多