Oli*_*liv 10 c++ language-lawyer template-argument-deduction c++17
作为这个问题的后续,我测试了clang和gcc的行为.似乎两个编译器对c ++标准有不同的解释.
在下面的示例中,如果需要根据推理指南假设的构造函数参数复制不可复制的参数,则GCC拒绝编译.Clang不执行此检查:
#include <cstddef>
struct not_copyable{
not_copyable()=default;
not_copyable(const not_copyable&)=delete;
};
struct movable{
movable()=default;
movable(movable&&);
};
template <typename T, size_t N>
struct A
{ template <typename ... Ts> A (Ts const & ...) {} };
template <typename T, size_t N>
struct B
{ template <typename ... Ts> B (const Ts & ...) {} };
template <typename T, typename ... Ts>
A(T const &, Ts const & ...) -> A<T, 1U + sizeof...(Ts)>;
template <typename T, typename ... Ts>
B(T, Ts ...) -> B<T, 1 + sizeof...(Ts)>;
int main()
{
not_copyable nc;
movable m;
auto a0 = A{nc}; // gcc & clang -> compile
auto a1 = A{m}; // gcc & clang -> compile
auto b0 = B{nc}; // clang ->compile; gcc -> error
auto b1 = B{m}; // clang ->compile; gcc -> error
}
Run Code Online (Sandbox Code Playgroud)
认为正确的行为是在C++标准的这一段中定义的[over.match.class.deduct]/2:
初始化和重载解析按照[dcl.init]和[over.match.ctor],[over.match.copy]或[over.match.list](适用于所执行的初始化类型)中的描述执行假设类类型的对象,其中所选择的函数和函数模板被认为是该类类型的构造函数,用于形成过载集,[...]
我强调" 为了形成一个过载集 ",因为我认为这是clang和gcc分歧的地方.Clang似乎没有检查演绎指导假设构造函数是否是一个可行的函数,但gcc确实如此.哪个编译器是对的?
Clang似乎没有检查演绎指导假设构造函数是否是一个可行的函数,但gcc确实如此.
实际上,演绎指南是可行的功能.一个可行的函数只意味着参数的数量匹配,约束得到满足,并且您可以为每个参数/参数对形成隐式转换序列.当我们检查ICS是否存在时,[over.best.ics]/2:
其他属性(例如生命周期,存储类,对齐,参数的可访问性,参数是否为位字段以及是否删除函数)将被忽略.
删除一个函数并不会使它不可行是非常重要的,因为它最终仍然是最好的可行候选者是很重要的.这意味着not_copyable删除复制构造函数的事实只有在我们实际调用它时才会生效.
例如,gcc和clang都拒绝这个程序.尽管删除了复制构造函数,但它#1是一个可行的候选者,它是最好的候选者:
struct NC {
NC() = default;
NC(NC const&) = delete;
NC& operator=(NC const&) = delete;
};
void foo(NC ); // #1
template <typename T> void foo(T const&); // #2
int main() {
NC nc;
foo(nc);
}
Run Code Online (Sandbox Code Playgroud)
但是我们实际上从来没有调用我们用于演绎的合成函数和函数模板.我们只是执行重载决策并选择最佳候选者 - 我们只用它来选择类类型,然后我们重新开始.我们在任何时候都不应该要求复制.
我认为这是一个gcc bug.提起86439.
| 归档时间: |
|
| 查看次数: |
257 次 |
| 最近记录: |