我希望有一个这样的类:
template<typename T>
struct Foo {
T* data_;
template<typename... Ts, std::enable_if<std::is_same<T,Ts>...>...>
explicit Foo(Ts...ts) : data_{ ts... } {}
};
但是,语法有问题,我不确定在初始化时是否可以像这样直接将参数设置到指针中。
我想要做的很简单:
Foo<int> f1{ 1, 3, 5, 7 }; // Or
// Foo<int> f1( 1, 3, 5 7 );
// f1.data_[0] = 1
// f1.data_[1] = 3
// f1.data_[2] = 5
// f1.data_[3] = 7
// f1.data_[4] = ... not our memory either garbage or undefined...
Foo<float> f2{ 3.5f, 7.2f, 9.8f }; // Or
// Foo<float> f2( 3.5f, 7.2f, 9.8f );
// f2.data_[0] = 3.5
// f2.data_[1] = 7.2
// f2.data_[2] = 9.8
// f2.data_[3] = ... not our memory
我还希望让构造函数进行检查,以确保传递给构造函数的每个参数都是<T>
类型;简单地说,对于每个Ts
,它必须是一个T
。
我可能想得太多了,但对于我来说,我不能得到这个或类似的东西来编译。我不知道它是在enable_if
、is_same
中,还是通过类的初始化器列表并试图将内容存储到指针中。我不知道是否应该使用T
数组,但是在将参数传递到构造函数之前,数组的大小是未知的。我还试图在不使用std::vector
等基本容器的情况下实现这一点;它更多的是为了自我教育,而不是为了实用的源代码。我只想看看如何使用原始指针来实现这一点。
编辑
我已经将我的类改为如下所示:
template<typename T>
struct Foo {
T* data_;
template<typename... Ts, std::enable_if_t<std::is_same<T, Ts...>::value>* = nullptr>
explicit Foo( const Ts&&... ts ) : data_{ std::move(ts)... } {}
};
在尝试使用它时:
int a = 1, b = 3, c = 5, d = 7;
Foo<int> f1( a, b, c, d );
Foo<int> f2{ a, b, c, d };
我在这个迭代中比较接近;但是它们都给出了不同的编译器错误。
C2661
:“没有重载函数采用4 arguments"C2440
:”正在初始化,无法从初始化器列表转换为容器,没有构造函数可以采用源类型,或者构造函数重载解析不明确。“发布于 2019-05-26 15:39:29
std::is_same
只比较两种类型,不能使用包扩展来声明多个模板参数。这意味着您需要将所有的std::is_same
检查都拉出到另一个检查中:
template <typename T, typename... Ts>
struct all_same : std::bool_constant<(std::is_same<T, Ts>::value && ...)> {};
template <typename T>
struct Foo
{
std::vector<T> data_;
template <typename... Ts, std::enable_if_t<all_same<T, std::decay_t<Ts>...>::value>* = nullptr>
Foo(Ts&&... ts)
: data_{std::forward<Ts>(ts)...}
{
}
};
您还需要为data_
数组分配内存。在这里,我使用了std::vector
来为我处理这个分配,但是如果您真的想要的话,也可以使用new[]
和delete[]
自己来管理它。
发布于 2019-05-26 16:02:27
enable_if
和is_same
不会在任何地方存储任何内容,它们只是编译时构造,不会对二进制可执行文件中的任何代码产生影响。
不管语法如何,您的代码实际上所做的是尝试获取构造函数参数的地址(这是一个临时参数)。一旦构造函数退出,这将是一个悬空指针。
要么Foo
拥有内存区,并且必须在构造函数中分配,要么在析构函数中删除(如果有任何疑问:使用std::vector
!),或者它为一些外部内存设置了别名,并且必须接收指向该内存的指针。
现在关于语法:
std::is_same
是一个提供value
布尔常量的模板,使用方法如下:std::is_same<T1, T2>::value
。或者,仅当常量表达式(第1个模板参数)为真时,才可以使用std::is_same_v<T1, T2>
.std::enable_if
提供type
类型成员。像使用std::enable_if<expr, T>::type
一样使用它。如果expr
为true,则type
是T
的类型定义。否则,它将不被定义,并产生替换失败。或者,您可以使用std::enable_if_t<expr, T>
你可以看看here,看看你的类似方法。
但是您也可以通过使用成员向量来简化这一切。在这种情况下,向量构造函数确保所有参数都具有兼容的类型。下面是一个完整的示例:
#include <string>
#include <vector>
#include <iostream>
#include <type_traits>
using namespace std;
template<typename T>
struct Foo {
vector<T> data_;
template<typename ...Ts>
explicit Foo(Ts... ts) : data_{ ts... } {}
void print() {
for (const auto &v : data_) {
cout << v << " ";
}
cout << endl;
}
};
int main() {
Foo<int> ints { 1, 2, 3, 4, 5 };
Foo<string> strings { "a", "b", "c", "d", "e"};
// Foo<string> incorrect { "a", 2, "c", 4, "e"};
ints.print();
strings.print();
// incorrect.print();
return 0;
}
发布于 2019-05-27 22:34:57
在C++17中,最常用的方法是使用std::cunjunction_v
。它懒惰地计算后续值,并允许您避免使用折叠表达式。此外,对于您在编辑的代码片段中提到的两种情况,编译器生成的消息也是相同的。
此外,通过const-rvalue-ref传递数据也没有意义,因为不可能从const对象中移动数据。另外,你正在将一组参数移动到一个指针。我不知道该怎么做,所以就把它删除了。
另外,看看std::decay_t
--如果没有它,它将无法工作,因为有时Ts
不会被推断为int
,而是const int &
或int &
。这会导致std::is_same_v
为false。
下面的代码在godbolt上编译得很好:
template<typename T>
struct Foo {
T* data_;
template<
typename... Ts,
std::enable_if_t<
std::conjunction_v<
std::is_same<T, std::decay_t<Ts>>...
>
> * = nullptr
>
explicit Foo( Ts&&... ts ) : data_{ } {}
};
https://stackoverflow.com/questions/56311239
复制相似问题