模板化构造函数的构造函数优先级

问题描述 投票:0回答:1

我正在寻找创建一个包装指针

SpecialPtr
,它遵循 5 规则,但下面还有一个模板化构造函数。当在向量中使用包装指针时,向量类想要使用模板化构造函数,从而导致像这样的编译错误

<source>:22:9: error: no matching function for call to 'Foo::Foo(SpecialPtr<Foo>&)'
   22 |         new T(std::forward<F>(first), std::forward<Args...>(args)...);
      |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:7:14: note: candidate: 'Foo::Foo(int)'
    7 |     explicit Foo(int x): val(x) {}
      |              ^~~
<source>:7:22: note:   no known conversion for argument 1 from 'SpecialPtr<Foo>' to 'int'
    7 |     explicit Foo(int x): val(x) {}

完整链接https://godbolt.org/z/nMjExMT1a

这让我感到困惑,因为我希望非模板化构造函数优先于它。下面的代码之所以有效,是因为添加了

requires
,为什么有必要?

#include <vector>
#include <memory>

using namespace std;

struct Foo {
    explicit Foo(int x): val(x) {}
    int val;
};

template<typename T>
struct SpecialPtr {
    SpecialPtr() noexcept = default;
    SpecialPtr(const SpecialPtr&) = default;
    SpecialPtr(SpecialPtr&&) = default;
    ~SpecialPtr() = default;
    SpecialPtr& operator=(const SpecialPtr&) = default;
    SpecialPtr& operator=(SpecialPtr&&) = default;
    
    template<typename F, typename... Args>
    // Why is this requires necessary?
    requires(!std::derived_from<std::decay_t<F>, SpecialPtr>)
    explicit SpecialPtr(F&& first, Args&&... args) {
        new T(std::forward<F>(first), std::forward<Args...>(args)...);
    }
    T* ptr;
};



int main () {
    vector<SpecialPtr<Foo>> v;
    vector<SpecialPtr<Foo>> v_2;
    v_2 = v; // Entrypoint for the error

    return 0;
}
c++ constructor c++20
1个回答
0
投票

代码可以简化

    SpecialPtr<Foo> v;
    SpecialPtr<Foo> v_2(v);

当编译器从

v_2
复制构造
v
时,它会尝试将构造函数与参数类型
SpecialPtr<Foo>&
相匹配。构造函数模板是最佳匹配,因为它不需要任何参数转换。因此,编译器选择完全匹配的模板,并且无法使用其参数。

复制构造函数

SpecialPtr(const SpecialPtr&)
匹配得不太好,因为它需要参数转换
SpecialPtr<Foo>&
const SpecialPtr<Foo>&

您可以添加重载的复制构造函数

    SpecialPtr(SpecialPtr&) = default;

它将与参数完全匹配,并且将在模板之前选择自由函数。

https://godbolt.org/z/zTWTWex6c

© www.soinside.com 2019 - 2024. All rights reserved.