`typename T::value_type` 应该被定义后面的约束拒绝时编译失败吗?
Should `typename T::value_type` fail to compile when it is meant to be rejected by a constraint that comes later in the definition?
以下代码:
#include <concepts>
template <typename T>
struct Foo
{
template <std::convertible_to<typename T::value_type> U> requires requires { typename T::value_type; }
void bar()
{
}
template <typename U>
void bar()
{
}
};
int main()
{
auto foo = Foo<float> {};
foo.bar<int>();
}
被 GCC 11 拒绝:
error: ‘float’ is not a class, struct, or union type
8 | void bar()
| ^~~
我期望发生的是 bar
的第一个定义由于不满足约束而被拒绝,而第二个定义被选中。但是,显然当 GCC 试图用 float
替换 T
时,它在查看约束之前无法编译 typename T::value_type
。如果将定义替换为:
,我可以获得预期的行为
template <typename U> requires (requires { typename T::value_type; } && std::convertible_to<U, typename T::value_type>)
void bar()
{
}
我觉得不太优雅。
标准是说第一种方法是非法的还是 GCC 实现中的缺陷?如果是前者,是否有更好的方式来编写此约束(除了定义一个新的命名概念,如 convertible_to_value_type_of
)?
编辑: 只是为了根据评论和(现已删除的)答案进行澄清:我理解为什么这段代码会根据 C++20 之前的规则被拒绝.我的意思是,向 C++20 添加概念可能是放宽规则的机会,以便编译器推迟对 typename T::value_type
之类的有效性的验证,直到它检查可能出现的约束在定义的其余部分。我的问题真的是:规则是以这种方式放松的吗?
标准很明确,约束只代入at the point of use or when needed for declaration matching:
The type-constraints and requires-clause of a template
specialization or member function are not instantiated along with the
specialization or function itself, even for a member function of a
local class; substitution into the atomic constraints formed from them
is instead performed as specified in [temp.constr.decl] and
[temp.constr.atomic] when determining whether the constraints are
satisfied or as specified in [temp.constr.decl] when comparing
declarations.
这是一个GCC bug。看来 GCC 确实在 requires-clause 中正确处理了这个问题,因此这可以作为一种解决方法:
template <class U>
requires std::convertible_to<U, typename T::value_type>
void bar()
{
}
以下代码:
#include <concepts>
template <typename T>
struct Foo
{
template <std::convertible_to<typename T::value_type> U> requires requires { typename T::value_type; }
void bar()
{
}
template <typename U>
void bar()
{
}
};
int main()
{
auto foo = Foo<float> {};
foo.bar<int>();
}
被 GCC 11 拒绝:
error: ‘float’ is not a class, struct, or union type
8 | void bar()
| ^~~
我期望发生的是 bar
的第一个定义由于不满足约束而被拒绝,而第二个定义被选中。但是,显然当 GCC 试图用 float
替换 T
时,它在查看约束之前无法编译 typename T::value_type
。如果将定义替换为:
template <typename U> requires (requires { typename T::value_type; } && std::convertible_to<U, typename T::value_type>)
void bar()
{
}
我觉得不太优雅。
标准是说第一种方法是非法的还是 GCC 实现中的缺陷?如果是前者,是否有更好的方式来编写此约束(除了定义一个新的命名概念,如 convertible_to_value_type_of
)?
编辑: 只是为了根据评论和(现已删除的)答案进行澄清:我理解为什么这段代码会根据 C++20 之前的规则被拒绝.我的意思是,向 C++20 添加概念可能是放宽规则的机会,以便编译器推迟对 typename T::value_type
之类的有效性的验证,直到它检查可能出现的约束在定义的其余部分。我的问题真的是:规则是以这种方式放松的吗?
标准很明确,约束只代入at the point of use or when needed for declaration matching:
The type-constraints and requires-clause of a template specialization or member function are not instantiated along with the specialization or function itself, even for a member function of a local class; substitution into the atomic constraints formed from them is instead performed as specified in [temp.constr.decl] and [temp.constr.atomic] when determining whether the constraints are satisfied or as specified in [temp.constr.decl] when comparing declarations.
这是一个GCC bug。看来 GCC 确实在 requires-clause 中正确处理了这个问题,因此这可以作为一种解决方法:
template <class U>
requires std::convertible_to<U, typename T::value_type>
void bar()
{
}