基于数组大小有效性的专业化
Specialization based on array size validity
尝试根据数组大小的有效性进行专门化:
// base template
template<int p, typename T = void>
struct absolute {
operator int () const { return 0; }
};
// positive case template
template<int p>
struct absolute<p, typename std::void_t<int[p]>> {
operator int () const { return p; }
};
// negative case template
template<int p>
struct absolute<p, typename std::void_t<int[-p]>> {
operator int () const { return -p; }
};
int main() {
std::cout << absolute<5>() << std::endl;
std::cout << absolute<-5>() << std::endl;
std::cout << absolute<0>() << std::endl;
}
问题 #1:
以上代码works nicely with gcc but fails to compile with clang.
Clang 生成错误:重新定义模板结构 'absolute'
谁说得对?
问题 #2:
Both with gcc and with clang (if we remove the negative specialization to bring clang back to the game), not clear why absolute<0>()
selects the base template. There is nothing wrong 与 int[0]
以及 std::void_t<int[0]>
似乎更专业:
// base template
template<int p, typename T = void>
struct absolute {
operator int () const { return -1; }
};
// positive case template
template<int p>
struct absolute<p, typename std::void_t<int[p]>> {
operator int () const { return p; }
};
int main() {
std::cout << absolute<5>() << std::endl; // 5
std::cout << absolute<0>() << std::endl; // -1, why not 0?
}
并且...如果基本模板只是声明而没有实现,如:
// base template
template<int p, typename T = void>
struct absolute;
Both gcc and clang would fail to compile,抱怨调用不完整类型无效:absolute<0>()
。尽管它似乎适合特殊情况。
这是为什么?
关于Clang的重定义错误,参见。
别名模板(例如 std::void_t
)的原始模板 ID 将简单地替换为它们的别名类型,而不检查替换失败的参数。这已更改为 CWG issue 1558. This only changed the standard to require substitution failure in the template arguments to be done, but doesn't clarify whether two templates that would be equivalent after replacing the alias should be considered equivalent. Clang considers them to be equivalent, but GCC doesn't. This is the open CWG issue 1980。
对于 -pedantic-errors
,GCC 已经为
报告了一个硬错误
std::cout << absolute<5>() << std::endl;
在专业化
template<int p>
struct absolute<p, typename std::void_t<int[-p]>>
因为数组大小应该不是常量表达式。数组的大小必须是 std::size_t
类型的转换常量表达式。转换后的常量表达式只能使用非收缩转换。所以 -p
与 p = 5
转换为 std::size_t
确实不是常量表达式,使得类型 int[-p]
格式错误,但我认为这应该导致替换失败,不是硬错误。 C++17 标准(草案 N4659)的 [temp.deduct/8] 说:
If a substitution results in an invalid type or expression, type deduction fails. An invalid type or expression is one that would be ill-formed, with a diagnostic required, if written using the substituted arguments.
这适用于此。引用后给出的非规范示例甚至包括负数数组大小作为替换失败的示例。
特别奇怪的是,对于 absolute<-5>()
,GCC 不 报告
上的等效错误
template<int p>
struct absolute<p, typename std::void_t<int[p]>>
专业化,其中 int[p]
的计算结果为 int[-5]
,它也没有转换后的常量表达式大小。
absolute<0>()
选择主模板,因为数组大小需要大于零,这使得偏特化都不可行。零大小数组是一种语言扩展,可以在 GCC 和 Clang 中使用 -pedantic-errors
禁用。
尝试根据数组大小的有效性进行专门化:
// base template
template<int p, typename T = void>
struct absolute {
operator int () const { return 0; }
};
// positive case template
template<int p>
struct absolute<p, typename std::void_t<int[p]>> {
operator int () const { return p; }
};
// negative case template
template<int p>
struct absolute<p, typename std::void_t<int[-p]>> {
operator int () const { return -p; }
};
int main() {
std::cout << absolute<5>() << std::endl;
std::cout << absolute<-5>() << std::endl;
std::cout << absolute<0>() << std::endl;
}
问题 #1:
以上代码works nicely with gcc but fails to compile with clang.
Clang 生成错误:重新定义模板结构 'absolute'
谁说得对?
问题 #2:
Both with gcc and with clang (if we remove the negative specialization to bring clang back to the game), not clear why absolute<0>()
selects the base template. There is nothing wrong 与 int[0]
以及 std::void_t<int[0]>
似乎更专业:
// base template
template<int p, typename T = void>
struct absolute {
operator int () const { return -1; }
};
// positive case template
template<int p>
struct absolute<p, typename std::void_t<int[p]>> {
operator int () const { return p; }
};
int main() {
std::cout << absolute<5>() << std::endl; // 5
std::cout << absolute<0>() << std::endl; // -1, why not 0?
}
并且...如果基本模板只是声明而没有实现,如:
// base template
template<int p, typename T = void>
struct absolute;
Both gcc and clang would fail to compile,抱怨调用不完整类型无效:absolute<0>()
。尽管它似乎适合特殊情况。
这是为什么?
关于Clang的重定义错误,参见
别名模板(例如 std::void_t
)的原始模板 ID 将简单地替换为它们的别名类型,而不检查替换失败的参数。这已更改为 CWG issue 1558. This only changed the standard to require substitution failure in the template arguments to be done, but doesn't clarify whether two templates that would be equivalent after replacing the alias should be considered equivalent. Clang considers them to be equivalent, but GCC doesn't. This is the open CWG issue 1980。
对于 -pedantic-errors
,GCC 已经为
std::cout << absolute<5>() << std::endl;
在专业化
template<int p>
struct absolute<p, typename std::void_t<int[-p]>>
因为数组大小应该不是常量表达式。数组的大小必须是 std::size_t
类型的转换常量表达式。转换后的常量表达式只能使用非收缩转换。所以 -p
与 p = 5
转换为 std::size_t
确实不是常量表达式,使得类型 int[-p]
格式错误,但我认为这应该导致替换失败,不是硬错误。 C++17 标准(草案 N4659)的 [temp.deduct/8] 说:
If a substitution results in an invalid type or expression, type deduction fails. An invalid type or expression is one that would be ill-formed, with a diagnostic required, if written using the substituted arguments.
这适用于此。引用后给出的非规范示例甚至包括负数数组大小作为替换失败的示例。
特别奇怪的是,对于 absolute<-5>()
,GCC 不 报告
template<int p>
struct absolute<p, typename std::void_t<int[p]>>
专业化,其中 int[p]
的计算结果为 int[-5]
,它也没有转换后的常量表达式大小。
absolute<0>()
选择主模板,因为数组大小需要大于零,这使得偏特化都不可行。零大小数组是一种语言扩展,可以在 GCC 和 Clang 中使用 -pedantic-errors
禁用。