要理解这个问题,请先阅读此答案.
我检查了不同的历史make_tuple实现(包括2012年的clang版本).在C++ 17之前我会期望它们,return {list of values ... }但它们都会在返回之前构造元组.它们都是非常简化的当前cppreference示例:
template <class... Types>
auto make_tuple(Types&&... args)
{
return std::tuple<special_decay_t<Types>...>(std::forward<Types>(args)...);
}
Run Code Online (Sandbox Code Playgroud)
它没错,但返回大括号初始化的重点是直接构造返回的对象.在C++ 17之前,没有保证的复制省略甚至在概念上消除了临时性.但即使使用C++ 17,我也不一定会期望花括号在这个例子中消失.
为什么在任何C++ 11/14实现中都没有花括号?换句话说,为什么不呢
template <class... Types>
std::tuple<special_decay_t<Types>...> make_tuple(Types&&... args)
{
return {std::forward<Types>(args)...};
}
Run Code Online (Sandbox Code Playgroud) 我正在使用C++ 17和Visual C++ 2017,我正在创建一个std::tuple引用std::forward_as_tuple.
从C++ 14开始,可以通过使用类类型而不是索引来访问元组的元素.
当我尝试下面的代码时,我有编译错误
error C2338: duplicate type T in get<T>(tuple)
Run Code Online (Sandbox Code Playgroud)
你知道如何访问以这种方式创建的元组中的元素吗?
下面是示例代码
struct CA {
int data_ = 0;
};
struct CB {
int data_ = 0;
};
int main()
{
CA a;
CA b;
auto joined_objects = std::forward_as_tuple(a, b);
std::cout << std::get<0>(joined_objects).data_ << std::endl; // works
std::cout << std::get<CA &>(joined_objects).data_ << std::endl; // fails
}
Run Code Online (Sandbox Code Playgroud) template<typename T>
void print(T& t)
{
std::cout << t << std::endl;
}
template<typename ... Args>
class Container
{
public:
Container(Args&& ... args)
: values_(std::forward<Args>(args)...)
{}
template<int INDEX>
typename std::tuple_element<INDEX, std::tuple<Args...> >::type& get()
{
return std::get<INDEX>(values_);
}
void display()
{
// (obviously) does not compile !
std::apply(print,values_);
}
private:
std::tuple<Args ...> values_;
};
Run Code Online (Sandbox Code Playgroud)
上面的代码显示了意图但不正确(注释的地方),因为函数“print”需要一个模板。
有没有办法将(适当模板化的)打印函数调用到元组 values_ 的每个元素?
运行代码:https : //onlinegdb.com/SJ78rEibD
std::tuple我认为通过std::make_tuplerequire 右值引用参数( type )创建一个非常有意识的设计决策T&&。
然而,这意味着对象类型的移动语义 ( std::move 只不过是 to 的强制转换T&&),而且我对构建始终需要这样做的对象感到不太舒服std::tuple。
int x = 7, y = 5;
std::tuple<int, int> fraction = make_tuple<int, int>( x, y ); // fails
Run Code Online (Sandbox Code Playgroud)
关于上述内容,编译器说:
错误 C2664: 'std::tuple<int,int> std::make_tuple<int,int>(int &&,int &&)': 无法将参数 1 从 'int' 转换为 'int &&'
message : 您无法将左值绑定到右值引用
std::tuple如果您不使用,则可以从左值制作 a没有问题make_tuple:
std::tuple<int, int> fraction = { x, y }; // ok
Run Code Online (Sandbox Code Playgroud)
我的问题是,这是为什么?
在这里http://en.cppreference.com/w/cpp/utility/tuple/tuple_element给出了std :: tuple_element的可能实现.
template< std::size_t I, class T >
struct tuple_element;
// recursive case
template< std::size_t I, class Head, class... Tail >
struct tuple_element<I, std::tuple<Head, Tail...>>
: std::tuple_element<I-1, std::tuple<Tail...>> { };
// base case
template< class Head, class... Tail >
struct tuple_element<0, std::tuple<Head, Tail...>> {
typedef Head type;
};
Run Code Online (Sandbox Code Playgroud)
但是,如果元组有很多参数(超过100或200个参数),这个实现需要深度递归实例化.
Q1:为什么C++ 11没有添加特殊运算符来获取索引元素?像元组[2]或元组[0]?
Q2:有可能减少深度实例化吗?例如,在D语言中,更多模板算法(在typetuple中)需要O(log(N))深度实例化.
编辑:Q1:为什么C++ 11没有添加特殊运算符来从变量模板获取索引元素?like template <class ... T> struct index {typedef T [3] third_element;}
C++ 11添加了非常有用的容器std :: tuple,现在我可以将许多结构转换为std :: tuple:
// my Field class
struct Field
{
std::string path;
std::string name;
int id;
int parent_id;
int remote_id;
};
//create
Field field = {"C:/", "file.txt", 23, 20, 41 };
//usage
foo( field.path );
field.name= new_name;
int id = field.id;
Run Code Online (Sandbox Code Playgroud)
至
//to std::tuple, /--path, /--name /--id, /--parend_id, /--remote_id
using Field = std::tuple< std::string, std::string , int, int , int >;
//create
auto field = make_tuple<Field>("C:\", "file.txt", 23, 20, 41);
// usage
foo( std::get<0>(field) ); // may …Run Code Online (Sandbox Code Playgroud) 假设我有一个元组和一个函数:
typedef std::tuple< std::unqiue_ptr<int>, std::unqiue_ptr<char> > SomeTuple;
void someFunction( std::unqiue_ptr<int>, std::unqiue_ptr<char> );
Run Code Online (Sandbox Code Playgroud)
所以在辅助函数中我将元组展开为参数:
void unroll( SomeTuple &t )
{
someFunction( std::get<0>( std::move( t ) ), std::get<1>( std::move( t ) ) );
}
Run Code Online (Sandbox Code Playgroud)
它有效,但我想避免重复std::move多次.天真的解决方案如:
void unroll( SomeTuple &t )
{
auto &&rt = std::move( t );
someFunction( std::get<0>( rt ), std::get<1>( rt ) );
}
Run Code Online (Sandbox Code Playgroud)
显然不起作用,因为rt是一个lvalue.那么有没有办法避免std::move()每次重复多次std::get?
我正在写一个小型库,它有一些API函数返回两个东西(不同类型).我宁愿不为此声明一个结构; 所以我想回来一个std::pair<foo, bat>.但是 - 也许在这些现代时代我宁愿回来std::tuple<foo, bar>呢?
更一般地说,什么时候应该优先选择元组,何时配对更合适的构造?
我想测试这个非常有趣的答案,并提出了这个最小的实现:
class A
{
enum M { a };
std::tuple<int> members;
public:
A() { std::get<M::a>(members) = 0; }
A(int value) { std::get<M::a>(members) = value; }
A(const A & other) { members = other.members; }
int get() const { return std::get<M::a>(members); }
bool operator==(A & other) { return members == other.members; }
};
Run Code Online (Sandbox Code Playgroud)
一个简单的测试:
int main() {
A x(42);
A y(x);
std::cout << (x==y) << std::endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
一切都很好,直到我定义一个简单的struct B {};并尝试添加它的一个实例作为成员.我一写
std::tuple<int, B> members;
Run Code Online (Sandbox Code Playgroud)
这operator== …
我有一个构建器类,我想将参数存储为引用,以便在后续构建中使用。
我想将可变数量的参数传递给我的类,使用类模板参数推导来推断模板参数,并将这些传递的参数作为引用存储在std :: tuple中。
从参数包转换为引用的std :: tuple的最简单方法是什么?
我发现std :: forward_as_tuple的功能与我想要的类似,但是我不想要转发引用,而且它在成员元组的初始化期间提供了语法错误。
template <typename... Ts>
struct Builder
{
using ArgsT = decltype(std::forward_as_tuple(Ts{}...));
ArgsT args_;
Builder(Ts&... ts) : args_(ts...) {}
};
int main()
{
struct Foo
{
int a;
double b;
};
Foo foo{};
Builder fooBuilder{foo.a, foo.b};
}
Run Code Online (Sandbox Code Playgroud)
语法错误是:
错误:没有匹配的调用函数
std::tuple<int&&, double&&>::tuple(int&, double&)