根据类模板参数对类成员使用不同的构造函数

Use different constructor for class member based on class template parameter

提问人:user1470475 提问时间:6/14/2023 最后编辑:user1470475 更新时间:6/14/2023 访问量:52

问:

存在这样一种情况:模板化类包含模板化成员,并且必须使用正确的构造函数(取决于类模板)构造该成员对象。简化示例:

#include <stdexcept>
#include <memory>
#include <iostream>
#include <vector>

template<int N=0>
struct A
{
    const int n_;

    A(int n, double)
        : n_{n}
    {
        static_assert(N <= 0);
    }

    A(double)
        : n_{N}
    {
        static_assert(N > 0);
    }
};


template<int N=0>
struct B
{
    const std::vector<int> m_;

    // Solution with unique_ptr class member to an A<N> object
    std::unique_ptr<A<N>> a_uptr_;
    B(const std::vector<int>& m, double x)
        :   m_{m},
            a_uptr_{N > 0 ? std::make_unique<A<N>>(x) : std::make_unique<A<N>>(m.size(), x)}
    {}


    // Solution with class member A<N> object
    // A<N> a_;
    // B(const std::vector<int>& m, double x)
    //  :   m_{m},
    //      a_{  <????????????????????> }
    // {}
};

int main()
{
    std::vector<int> m{1,2,3};
    B b1(m, 0);
    B<2> b2(m, 0); 
    std::cout << b1.a_uptr_->n_ << ", " << b2.a_uptr_->n_ << std::endl;
}

Q1:有没有办法只有一个类成员(而不是唯一的 ptr)并让它调用正确的构造函数?使用也许???A<N>std::integer_sequence

(Q2:我是不是想多了,即我真的应该担心必须将 obj 放在堆上的性能吗?A<N>

C++ 构造函数 C++17 initializer-list

评论

1赞 Some programmer dude 6/14/2023
您可以使用 或 进行初始化。就像你已经对指针所做的那样。a_A<N>(x)A<N>(,.size(), x)
0赞 Some programmer dude 6/14/2023
也许你可以用专业化来代替?您试图解决的实际和潜在问题是什么?也许它有不同的解决方案?
0赞 user1470475 6/14/2023
编辑:对不起,如果调用了错误的构造函数,实际代码确实会产生编译时错误。所以不能像那样通过复制构造函数进行初始化。我会在问题中将 a 更改为 a。Athrowstatic_assert
0赞 Oersted 6/14/2023
根据您的实际目标,有几种解决方案是可能的。我首先考虑的是标签调度。您可以使用 N <= 0 和 N > 0 不同的模板标记,并将其作为构造函数的默认第二个参数传递。这能解决你的问题吗?A
0赞 Oersted 6/14/2023
转念一想,根据 N 的说法,签名已经不一样了......

答:

1赞 463035818_is_not_an_ai 6/14/2023 #1

假设问题只是关于,这应该不会改变...... 那么你面临的就是我所说的“初始值设定项列表中成员的复杂构造”,而解决方案通常是一个静态方法:BA

#include <stdexcept>
#include <iostream>
#include <vector>

template<int N=0>
struct A {
    const int n_;   
    A(int n, double) : n_{n} {
        if constexpr (N > 0) throw  std::runtime_error("wrong constructor for N > 0");
    }

    A(double) : n_{N} {
        if constexpr (N <= 0) throw std::runtime_error("wrong constructor for N <= 0");
    }
};


template<int N=0>
struct B {
    const std::vector<int> m_;    
    A<N> a_uptr_;
    B(const std::vector<int>& m, double x) 
      :   m_{m},
          a_uptr_{make_AN(m,x)}
    {}
private:
    static A<N> make_AN(const std::vector<int>& m,double x) {
        if constexpr (N>0) return x;
        else return {m.size(),x};
    }
};

int main() {
    std::vector<int> m{1,2,3};
    B b1(m, 0);
    B<2> b2(m, 0); 
    std::cout << b1.a_uptr_.n_ << ", " << b2.a_uptr_.n_ << std::endl;
}

或者正如评论中提到的(一些程序员家伙和 n.m.),简单地.也许你害怕无论如何都被省略的副本。a_{N > 0 ? A<N>{m} : A<N>{m, x}}

但是,您应该考虑重写,以便使用 和 调用 时出现编译时错误。AA<N>::A(int,double)N > 0A<N>::A(double)N <= 0

评论

0赞 Oersted 6/14/2023
@463035818_is_not_a_number,我对 .您能否详细说明使编译器使用适当构造函数的机制。特别是,第二个看起来像是从 到 的隐式转换,它没有定义,因此我一定是错的。returnmake_ANstd::initializer_listA<N>
1赞 463035818_is_not_an_ai 6/15/2023
@Oersted en.cppreference.com/w/cpp/language/list_initialization 案例 (8)
0赞 Oersted 6/15/2023
@463035818_is_not_a_number,为了正确,情况 (8) 说 ̀return {m.size(),x} ̀ 实际上与 ̀̀̀{m.size(),x}' 不是(允许混合类型)相同。我说得对吗?A<N>{m.size(),x}std::initializer_list