奇怪的是,转换构造函数不与复制构造函数竞争

Conversion constructor strangely not competing with copy constructor

提问人:Chris 提问时间:12/7/2022 更新时间:12/7/2022 访问量:64

问:

实现复制构造函数会删除 C++ 中的默认移动构造函数。 只有编译器生成的复制和移动构造函数是微不足道的。

创建了从任何类型到当前类型的模板化转换构造函数。

#include <format>

#include <iostream>
#include <type_traits>

template <typename T = int>
class Element {
   public:
    T value;

    Element(const T value_) noexcept : value(value_) {};

    // Here is the conversion constructor
    template <typename TT>
    Element(const Element<TT> &element) noexcept : value(element.value) {
        std::cout << std::format(
            "Element<{}>(const {}& {})\n", 
            typeid(T).name(), typeid(TT).name(), element.value
        );
    }

    // uncommenting this breaks the assertions coming next
    // Element(const Element &element) noexcept : value(element.value) {};
};

static_assert(std::is_trivially_move_constructible<Element<>>::value);
static_assert(std::is_trivially_copy_constructible<Element<>>::value);

// how it behaves
void foo_int(Element<int> element) { 
    std::cout << std::format("foo_int: {}\n", element.value); 
}

void foo_double(Element<double> element) { 
    std::cout << std::format("foo_double: {}\n", element.value); 
}

int main() {
    Element<int> int_element {1};
    Element<double> double_element {1.5};

    foo_int(int_element);
    foo_double(int_element);

    // uncommenting doesn't compile - narrowing conversion
    // foo_int(double_element);
    foo_double(double_element);

    return 0;
}

Compiler Explorer 中的演示

有人可以向我解释为什么这个转换构造函数与 不匹配。在这种情况下,仅调用复制/移动构造函数。T == TT

然后我想也许我可以用 .但这给出了一个错误:“过时的声明样式”。auto a = Element<int>::Element<int>(int_element);

它似乎被视为一个普通的构造函数,并且只在其他特殊成员函数之后考虑。

有人可以向我解释规则或在哪里可以阅读有关此行为的更多信息吗?

C++ 模板 复制构造函数

评论

1赞 NathanOliver 12/7/2022
如果那样,您没有转换,而是副本。如果要复制,则使用复制结构。TT == T
1赞 Jarod42 12/7/2022
[OT]:仍然允许通过 DemoElement(const Element &element) noexcept = default;static_assert
1赞 Daniel Langr 12/7/2022
[OT]:请注意,如果从模板化转换构造函数中删除,它将优先于复制构造函数:godbolt.org/z/4czT5W9vYconst
0赞 Chris 12/7/2022
@DanielLangr 哇,这太奇怪了。我测试了它,免费功能也是如此。仔细想想,更喜欢 in-out / non-const 参数是有道理的:godbolt.org/z/aeKdeoz45

答:

3赞 Chris Uzdavinis 12/7/2022 #1

当类型 T 与 TT 相同时,则您正在制作副本。该标准明确指出复制构造函数不是模板:

类 X 的非模板构造函数是复制构造函数,如果其 第一个参数是 X&、常量 X&、易失性 X& 或常量易失性 X&,要么没有其他参数,要么所有其他参数 参数具有默认参数 ([dcl.fct.default])。https://eel.is/c++draft/class.copy.ctor#1

此外,在 C++ 中,如果非模板完全匹配,则它始终优先于模板。假设有一个真正的复制构造函数和模板化转换构造函数,则在复制上下文中将选择复制构造函数。同样的事情也会发生在普通函数上:

template <typename T>
void foo(T);

void foo(int);

foo(123); // will call the non-template version (since it's an exact match)

评论

0赞 Chris 12/7/2022
谢谢,这正是我一直在寻找的。有了这个解释和更多的实验,它现在完全清楚了!