如何在 c++17 中禁用过于通用的转发构造函数并延迟复制构造函数 [duplicate]

How to disable overly generic forwarding constructor in c++17 and defer to copy constructor [duplicate]

提问人:bradgonesurfing 提问时间:3/27/2023 更新时间:3/27/2023 访问量:71

问:

如果我使用 c++20 概念编写简单类


#include <array>
#include <type_traits>

template <class T, int N>
struct MyVec {
    std::array<T, N> m_vec;

    template <typename... Q>
    MyVec(Q&&... args) 
    requires (std::is_same_v<Q,T> && ...): 
    m_vec{std::forward<Q>(args)...} {}
};

以及它被使用的例子

int main(int argc, char* argv[]) {
    using V = MyVec<double, 2>;
    V v0(1., 2.);
    V v1(1., 2.);

    V v2 = v0;
}

requires 语句可防止复制构造函数被过于泛型的转发构造函数隐藏。查看 https://godbolt.org/z/8Me19v7Ks

如果删除 requires 语句,则该行将无法编译。V v2 = v0

但是,我不能使用概念,因为这需要在仅限 c++17 的上下文中。我被困在试图弄清楚如何使用enable_if来保护构造函数。我可以将构造标记为显式,但我宁愿不这样做。

C++ C++17 复制构造函数 perfect-forwarding stdarray

评论

1赞 Jarod42 3/27/2023
你不也想要吗?sizeof...(Q) == N
0赞 Sergey Kolesnik 3/27/2023
它像一个不相关的小例子吗?否则为什么这么复杂? 在你愿意using V = std::array<double, 2>main
0赞 bradgonesurfing 3/27/2023
@SergeyKolesnik 新来?stackoverflow.com/help/minimal-reproducible-example
0赞 bradgonesurfing 3/27/2023
@Jarod42额外的约束可能是一个好主意,尽管它无论如何都会在相当早的时候正确地失败,并且不会对其他默认构造函数造成问题。
0赞 Sergey Kolesnik 3/27/2023
如果有一个“最小可行”的例子就好了,因为还有一种关于提出“XY问题”的趋势

答:

4赞 Holt 3/27/2023 #1

您可以使用禁用模板:enable_if

template <class T, int N>
struct MyVec {
    std::array<T, N> m_vec;

    template <typename... Q,
        std::enable_if_t<(std::is_convertible_v<Q, T> && ...), int> = 0>
    MyVec(Q&&... args) 
    : m_vec{std::forward<Q>(args)...} {}
};

我不是概念方面的专家,但大多数(全部?)你可以用或类似的东西来实现,尽管它通常有点丑陋(而且容易出错),并且会导致编译器的可读性大大降低。requiresenable_if

评论

0赞 bradgonesurfing 3/27/2023
那行得通。godbolt.org/z/5K438vEdM在过去的 6 个月里,我一直在使用概念,以至于忘记了如何做旧 skool。
1赞 Jarod42 3/27/2023 #2

我喜欢包装器的另一种选择是:std::array

template <typename T, std::size_t>
using always_t = T;

template <class T, class Seq>
struct MyVecImpl;

template <class T, std::size_t... Is>
struct MyVecImpl<T, std::index_sequence<Is...>>
{
    std::array<T, sizeof...(Is)> m_vec;

    MyVecImpl(always_t<T, Is>... ts) : m_vec{std::move(ts)...}{}
};

template <class T, int N>
using MyVec = MyVecImpl<T, std::make_index_sequence<N>>;

演示

不需要 SFINAE/concepts。

评论

0赞 bradgonesurfing 3/27/2023
这很有趣。为什么这样做?其中,这如何在 ts 上生成参数包。例如,这不起作用,godbolt.org/z/6j967n4Tj 我一直认为要生成参数包,需要在实际方法上有一个模板。
1赞 Holt 3/27/2023
@bradgonesurfing 不是参数包,因此不能使用 while is,因此可以使用 和 。请注意,在这种情况下,如果您想实现您已经拥有的内容,您将需要 和 版本,因为您不能将完美转发与 一起使用。TT...IsIs...always_t<T, Is>... &&const&always_t<T, Is>...