同一类的各种明确构造函数
Wide variety of unambiguous constructors for the same class
我有以下类:
class Foo
{
public:
// Constructors here
private:
std::vector<X> m_data; // X can be any (unsigned) integer type
};
我希望以下代码正常工作:
Foo f0;
Foo f1(1); // and/or f1({1})
Foo f2(1, 2); // and/or f2({1, 2})
Foo f3(1, 2, 3); // and/or f3({1, 2, 3})
Foo f4(1, 2, 3, 4); // ... and so on
std::vector<int> vec = {1, 2, 3, 4};
Foo f5(vec);
Foo f6(vec.begin(), vec.end());
std::list<std::size_t> list = {1, 2, 3, 4};
Foo f7(list);
Foo f8(list.begin(), list.end());
std::any_iterable_container container = {1, 2, 3, 4};
Foo f9(container);
Foo f10(container.begin(), container.end());
// PS: I guess I only want containers/iterators that store individual
// values and not pairs (e.g., I don't want care about std::map
// because it does not make sense anyway).
到目前为止,我已经尝试将 SFINAE、构造函数重载与所有可能的类型、可变参数模板等结合起来。每次我修复一个构造函数案例时,其他构造函数都会崩溃。此外,我编写的代码变得非常复杂且难以阅读。但是,问题似乎很简单,我想我只是以错误的方式接近它。任何关于如何编写构造函数的建议(最好是在 C++17 中),同时保持代码尽可能简单,都是非常受欢迎的。
谢谢。
实现f1
的最简单方法 -f4
(似乎接受可变数量的已知类型的参数T
不是容器或迭代器)是这样的:
template<typename... Args>
Foo(T arg, Args... args) {...}
由于此构造函数至少接受 1 个参数,因此默认构造函数f0
没有歧义。由于第一个参数是类型T
,因此与以下构造函数没有歧义。
如果要以不同于其他容器的方式处理std::vector
和std::list
,可以创建一个部分专用的帮助程序模板来检查参数是否是给定模板的实例:
template<typename>
struct is_vector : std::false_type {};
template<typename T, typename Allocator>
struct is_vector<std::vector<T, Allocator>> : std::true_type {};
并像这样使用它来实现f5
和f7
:
template<typename T, Constraint = typename std::enable_if<is_vector<typename std::remove_reference<T>::type>::value, void>::type>
Foo(T arg) {...}
通过测试std::vector
和std::list
的相应迭代器类型,您可以以相同的方式实现f6
和f8
。
您可以检查成员函数是否存在begin()
和end()
以实现f9
(我想)如下:
template<typename T>
Foo(T arg, decltype(arg.begin())* = 0, decltype(arg.end())* = 0) {...}
但是,必须显式禁用此构造函数,以便使用创建的帮助程序模板进行std::vector
和std::list
,以避免歧义。
要检查参数是否是实现f10
的迭代器,可以使用std::iterator_traits
:
template<typename T, typename Constraint = typename std::iterator_traits<T>::iterator_category>
Foo(T begin, T end) {...}
同样,您必须为std::vector
和std::list
的迭代器类型显式禁用此构造函数。
这个想法是像这样定义类:
template <typename X>
class Foo
{
public:
Foo() { };
Foo(initializer_list<int> l) :m_data(l) { };
template<typename container>
Foo(container const & c) :m_data(c.begin(), c.end()) {};
template<typename iterator>
Foo(iterator begin, iterator end) :m_data(begin, end) { };
private:
std::vector<X> m_data;
};
哪里:
Foo()
是默认(非参数)构造函数。
Foo(initializer_list<int> l)
接受像{1, 2, 3}
这样的列表。
Foo(container const & c)
接受任何支持begin
和end
迭代器的容器。
Foo(iterator begin, iterator end)
使用begin
和end
迭代器初始化类。
用法:
Foo<int> f0;
Foo<int> f1({1});
Foo<int> f2({1, 2});
Foo<int> f3({1, 2, 3});
Foo<int> f4({1, 2, 3, 4});
std::vector<int> vec = {1, 2, 3, 4};
Foo<int> f5(vec);
Foo<int> f6(vec.begin(), vec.end());
std::list<size_t> list = {1, 2, 3, 4};
Foo<size_t> f7(list);
Foo<size_t> f8(list.begin(), list.end());
set<unsigned> container = {1, 2, 3, 4};
Foo<unsigned> f9(container);
Foo<unsigned> f10(container.begin(), container.end());
假设类定义如下:
template <class T>
class Foo
{
public:
[..]
private:
std::vector<T> m_data;
}
让我们将此任务分解为子任务:
从迭代器构造
template <class Iterator>
Foo (Iterator begin, Iterator end, typename Iterator::iterator_category * = 0)
: m_data(begin, end);
我们将填写begin
和end
的m_data
。
第三个参数将确保只有声明iterator_category
Iterator
类型与此原型匹配。由于此参数的默认值为0
且从未指定,因此它仅在模板推导过程中起作用。当编译器检查这是否是正确的原型时,如果类型Iterator::iterator_category
不存在,它将跳过它。由于iterator_category
是每个标准迭代器的必备类型,因此它将适用于它们。
此 c'tor 将允许以下调用:
std::vector<int> vec = {1, 2, 3, 4};
Foo<int> f(vec.begin(), vec.end());
-- AND --
std::list<std::size_t> list = {1, 2, 3, 4};
Foo<int> f(list.begin(), list.end());
从容器构造
template <class Container>
Foo (const Container & container, decltype(std::begin(container))* = 0, decltype(std::end(container))* = 0)
: m_data(std::begin(container), std::end(container));
我们将从给定的容器中填充我们的m_data
。我们使用std::begin
和std::end
对其进行迭代,因为它们比它们的.begin()
和.end()
对应项更通用,并且支持更多类型,例如原始数组。 此 c'tor 将允许以下调用:
std::vector<int> vec = {1, 2, 3, 4};
Foo<int> f(vec);
-- AND --
std::list<std::size_t> list = {1, 2, 3, 4};
Foo<int> f(list);
-- AND --
std::array<int,4> arr = {1, 2, 3, 4};
Foo<int> f(arr);
-- AND --
int arr[] = {1, 2, 3, 4};
Foo<int> f(arr);
从初始值设定项列表构造
template <class X>
Foo (std::initializer_list<X> && list)
: m_data(std::begin(list), std::end(list));
注意:我们通常将列表作为 Rvalue 引用,但我们也可以添加一个Foo (const std::initializer_list<X> & list)
来支持 Lvalue 的构造。
我们通过再次迭代列表来填充我们的m_data
。这个c'tor将支持:
Foo<int> f1({1});
Foo<int> f2({1, 2});
Foo<int> f3({1, 2, 3});
Foo<int> f4({1, 2, 3, 4});
来自可变参数数量的构造函数
template <class ... X>
Foo (X ... args) {
int dummy[sizeof...(args)] = { (m_data.push_back(args), 0)... };
static_cast<void>(dummy);
}
在这里,将数据填充到容器中有点棘手。我们使用参数扩展来解包和推送每个参数。这个 c'tor 允许我们调用:
Foo<int> f1(1);
Foo<int> f2(1, 2);
Foo<int> f3(1, 2, 3);
Foo<int> f4(1, 2, 3, 4);
全班
最终的结果相当不错:
template <class T>
class Foo
{
public:
Foo () {
std::cout << "Default" << std::endl;
}
template <class ... X>
Foo (X ... args) {
int dummy[sizeof...(args)] = { (m_data.push_back(args), 0)... };
static_cast<void>(dummy);
std::cout << "VA-args" << std::endl;
}
template <class X>
Foo (std::initializer_list<X> && list)
: m_data(std::begin(list), std::end(list)) {
std::cout << "Initializer" << std::endl;
}
template <class Container>
Foo (const Container & container, decltype(std::begin(container))* = 0, decltype(std::end(container))* = 0)
: m_data(std::begin(container), std::end(container)) {
std::cout << "Container" << std::endl;
}
template <class Iterator>
Foo (Iterator first, Iterator last, typename Iterator::iterator_category * = 0)
: m_data(first, last) {
std::cout << "Iterators" << std::endl;
}
private:
std::vector<T> m_data;
};
- 获取从C++中同一类中的构造函数调用的方法返回的值
- 在 c++ 中将对象设置为等于同一类的构造函数是否有效?
- 将1D或2D向量传递给同一类的构造函数
- 让构造函数在其初始化列表中调用同一类的另一个构造函数是否有效
- 在C++中,类是否可以包含指向构造函数中初始化的同一类的指针?
- 为什么一个非平凡的成员需要为同一类中的匿名联合定义构造函数
- 如何在类构造函数内部对同一类的成员函数启动pthread
- 同一类的各种明确构造函数
- 从同一类的另一个构造函数调用构造函数
- 为什么在同一类中使用不同的(共享_ptr和正常)指针构造函数,我会得到不同的结果
- 用参数初始化另一类构造函数中的对象
- 一个嵌入在另一类 构造函数中的一个类的对象数组
- 不能从同一类的另一个构造函数调用具有所有默认参数的显式构造函数
- C++类构造函数使用指向同一类对象的指针
- C++是一个类的默认构造函数,调用另一类的另一个默认构造函数
- C++14从不同的构造函数(同一类)调用一个[具有不同参数]的构造函数
- 调用同一类中的另一个构造函数
- c++在同一类的另一个构造函数中调用构造函数
- 同一类的 C++ 扩展构造函数(无继承)
- 将类构造函数重载放在同一类的另一个重载中