c++ - 模板别名冲突类型。 g++ 编译成功而 clang 失败

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

我遇到了一个非常奇怪的编译器错误。由于某种原因,发布的代码确实可以使用 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:

A type alias declaration introduces a name which can be used as a synonym for the type denoted by type-id. It does not introduce a new type and it cannot change the meaning of an existing type name. There is no difference between a type alias declaration and typedef declaration. This declaration may appear in block scope, class scope, or namespace scope.

Alias templates are never deduced by template argument deduction when deducing a template template parameter.

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

最佳答案

借用@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 中指定的那样:

When a template-id refers to the specialization of an alias template, it is equivalent to the associated type obtained by substitution of its template-arguments for the template-parameters in the type-id of the alias template

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

T<int>U<int>是等价的,X<T<int>>也是和 Z<U<int>> , 但此规则不会扩展到别名模板 U相当于类模板T (就其本身而言,它们不是专业)。

这与别名 FieldData 的情况相同和类模板 ConcreteData .

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

关于c++ - 模板别名冲突类型。 g++ 编译成功而 clang 失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53485197/

相关文章:

c++ - 检查BST中每个节点的平衡因子并将其存储在节点中

c++ - OSX gcc 找不到已安装的库

c++ - GL_BLEND 不能与顶点数组一起正常工作

c++ - 在参数包中分配模板包装器

c++ - 为 std::tuple 重载运算符 << - 可能的简化?

c++ - 头文件中的类模板给我很多错误

c++ - 指向带有默认参数的函数的指针

c++ - 使用 std::unique_ptr/std::shared_ptr const 正确组合

c++ - main 中的 Void 并防止 Dos 程序死亡

c++ - 基于 C++ 中的 bool vector 选择 vector 中的对象