为什么模板参数不参与 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 遍,每个假设一个吗?
模板解析已经很慢、成本高且复杂。让它变得更复杂真的很难卖。
为什么禁止以下代码:
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 遍,每个假设一个吗?
模板解析已经很慢、成本高且复杂。让它变得更复杂真的很难卖。