为什么模板参数不参与 class definition/redefinition

why don't the template parameters participate to the class definition/redefinition

为什么禁止以下代码:

template <std::size_t N>
struct A
{

};

template <class T>
struct A// error: different template parameter => redeclaration
{

};

知道我们可以冗长地表达同样的想法:

template <class T>
concept C_TypePack = T::is_type_pack_concept;

template <class... Types>
struct TypePack {static constexpr bool is_type_pack_concept = true;};

template <class T>
concept C_NonTypePack = T::is_non_type_pack_concept;

template <auto... NonTypes>
struct NonTypePack {static constexpr bool is_non_type_pack_concept = true;};

template <class T>
concept C_TemplatePack = T::is_template_pack_concept;

template <C_TypePack TTypePack, C_NonTypePack TNonTypePack>
struct TemplatePack {static constexpr bool is_template_pack_concept = true;};

template <C_TemplatePack TP>
struct A;

template <std::size_t N>
struct A<TemplatePack<TypePack<>, NonTypePack<N>>>
{

};

template <class T>
struct A<TemplatePack<TypePack<T>, NonTypePack<>>>
{

};

我的问题是:为什么不允许这种代码:

template <any>
struct A;

template <std::size_t N>
struct A<N>
{

};

template <class T>
struct A<T>
{

};

我不关心“这是写在法律上”的方面。我只是想从编译器的角度来看歧义在哪里。以此类推,模板函数就好了。

编辑:感谢 n 的评论。 'pronouns' m.,这是使用 std::array 作为非类型的解决方法(元组也适用):

#include <array>

// type spe

template <auto Val>
struct A;

template <auto Size>
requires (std::is_same_v<decltype(Size), std::size_t>)
struct A<Size>
{

};

template <auto SizePack>
requires (std::is_same_v<typename decltype(SizePack)::value_type, std::size_t>)
struct A<SizePack>
{

};

// var spe

struct Foo
{
    template <auto Val>
    static consteval auto initBar()
    {
        if constexpr ( std::is_same_v<decltype(Val), std::size_t> )
            return 5.;
        else if constexpr ( requires{std::is_same_v<typename decltype(Val)::value_type, std::size_t>;} )
            return -5;
        else
            return -1;
    }

    template <auto Val>
    static constexpr auto bar = initBar<Val>();
};

int main()
{
    A<std::size_t(42)> a0;
    A<std::array<std::size_t, 1>{42}> a1;
    A<std::array<std::size_t, 2>{42, 43}> a2;

    Foo::bar<std::size_t(42)>;
    Foo::bar<std::array<std::size_t, 1>{42}>;
    Foo::bar<std::array<std::size_t, 2>{42, 43}>;

    return 0;
}

问题还是一样。

模板函数的特化和模板类型的特化是非常不同的。

函数参与重载决策。重载解析是 C++ 标准中极其复杂的 bug-causing 部分。它“像魔术一样工作”,因为为了让它工作已经做了很多工作,而且当它与模板代码交互时,它仍然以令大多数人惊讶的方式运行。

模板函数没有偏特化,只有全特化;专业化只改变使用的body,它永远不会改变选择的。有两种不同的方式来改变交互使用的实现将是疯狂的。

另一方面,模板类型使用不同的方式进行选择。这里使用了部分和完全特化,没有重载。

专业化是一种子类型化。主模板确定传递的参数是什么,而特化则不然。特化只是 匹配 与主模板的参数。

您的更改会引入一组新的不同主要专业。选择你专攻哪个模板的规则必须改变并变得异常复杂,并且为程序员确定你专攻哪个模板也将更加困难。

template<class T>
struct A;
template<int x>
struct A;

template<class T, class U>
struct A< T::template apply<U> > {};

快,我刚刚特化了哪个模板,A<int>还是A<class>?现在,我现在,但有多少人会对此感到惊讶?很多。

...

第二个问题与早期检查有关。

template <any>
struct A;

现在,当有人做 A<T::template apply<U>> 时,您无法检查自己是否搞砸了 types-vs-values,直到过程的后期。

还有什么

template<any x>
struct A: B<x> {};

现在我们必须在假设 x 是类型、值和模板的情况下解析 A<x> 的 body。如果其中只有 1 个或 2 个或 0 个对给定语句有效,这是否是一个错误?

怎么样

template<any x>
struct A {
  int y = x<3-x::green>;
};

如果 x 是一个类型,x::green 就有意义,但是现在 x<?> 只有当 x 是一个模板时才有意义。你做 3 遍,每个假设一个吗?

模板解析已经很慢、成本高且复杂。让它变得更复杂真的很难卖。