I learned that from C++17, with the deduction guides, template arguments of std::vector can be deduced e.g. from the initialization:
std::vector vec = { function_that_calculate_and_return_a_specifically_templated_type() }
However I do not have the luxury of C++17 in the machine where I want to compile and run the code now.
Is there any possible workaround for C++11? If more solutions exist, the best would be the one that keep the readability of the code.
At the moment the only idea that I have is to track the various cases along the code (luckily they should not be too many) and make some explicit typedef/using.
Any suggestion is very welcome
The usual way to use type deduction for class template when CTAD is not available is providing a make_* function template, e.g. for your case (trailing return type is necessary for C++11):
#include <vector>
#include <type_traits>
#include <tuple>
template <class ...Args>
auto make_vec(Args&&... args) ->
std::vector<typename std::decay<typename std::tuple_element<0, std::tuple<Args...>>::type>::type>
{
using First = typename std::decay<typename std::tuple_element<0, std::tuple<Args...>>::type>::type;
return std::vector<First>{std::forward<Args>(args)...};
}
You can invoke the above with
const auto v = make_vec(1, 2, 3);
which gets at least kind of close to CTAD in the sense that you don't have to explicitly specify the vector instantiation.
While the answer by lubgr is a correct way, the following template is simpler and seems to work as well:
#include <vector>
#include <string>
template <typename T>
std::vector<T> make_vec(const std::initializer_list<T> &list)
{
return std::vector<T>(list);
}
int main()
{
auto v = make_vec({1,2,3});
auto v2 = make_vec({std::string("s")});
std::string s("t");
auto v3 = make_vec({s});
return v.size() + v2.size() + v3.size();
}
One advantage of using the initializer_list template directly are more clear error messages if you pass mixed types like in make_vec({1,2,"x"});, because the construction of the invalid initializer list now happens in non-templated code.
This looks like an issue in clang (I've already opened a bug here), but I'd like to be sure that I'm not doing a mistake.
Consider the following code:
#include <type_traits>
#include <cstddef>
template<std::size_t N, std::size_t M, std::enable_if_t<not (N>M)>* = nullptr> // (1)
struct S: public S<N+1, M> { };
template<std::size_t N>
struct S<N, N> { };
int main() {
S<0, 1> c{};
}
It fails to compile with the following error:
8 : error: non-type template argument specializes a template parameter with dependent type 'std::enable_if_t M)> *' (aka 'typename enable_if M), void>::type *')
struct S { };
The same code works as expected using the following line instead of (1):
template<std::size_t N, std::size_t M, typename = std::enable_if_t<not (N>M)>>
The SFINAE expression is almost the same. It is based on a specialization of std::enable_if_t and I would expect the same result (success or failure) for both of the examples.
Are my expectations wrong?
Note that GCC works fine in either cases.
I think this is a gcc bug actually, as a result of [temp.class.spec]:
The type of a template parameter corresponding to a specialized
non-type argument shall not be dependent on a parameter of the
specialization. [ Example:
template <class T, T t> struct C {};
template <class T> struct C<T, 1>; // error
template< int X, int (*array_ptr)[X] > class A {};
int array[5];
template< int X > class A<X,&array> { }; // error
—end example ]
In your example, the type of the 3rd template parameter is dependent on a parameter. When you swap it to typename = std::enable_if_t<...>, then this rule no longer applies.
Note: is there any reason to use SFINAE here anyway, as opposed to static_assert-ing?
I have a std::tuple (or a boost fusion tuple) whose elements cannot be trivially constructed (for example references) and I want to iterate over the types but not the values of the elements.
In this example I have a (general) tuple type and I want to generate a vector with (runtime) type information. The example below works if all the types in the sequence are trivially default constructed but not in general.
In summary, I want a function that transform: std::tuple<...> -> std::vector<std::type_index>
#include <boost/fusion/adapted/std_tuple.hpp>
#include <boost/fusion/algorithm/iteration/for_each.hpp>
#include <typeindex>
#include<vector>
using tuple_type = std::tuple<std::string&, int>;
int main(){
std::vector<std::type_index> types;
boost::fusion::for_each(
tuple_type{}, // fails because of std::string&
[&types](auto& e){types.push_back(typeid(e));}
);
}
The problem is that I have to generate runtime information from non runtime information and I can't figure out how to mix the fusion functions (http://www.boost.org/doc/libs/1_59_0/libs/fusion/doc/html/fusion/algorithm/iteration/functions.html) and the metafunctions (http://www.boost.org/doc/libs/1_41_0/libs/fusion/doc/html/fusion/algorithm/iteration/metafunctions.html).
I tried with boost::fusion::accumulate and boost::fold but the situation is always the same, at some point I have to generate a runtime element in order to apply the algorithm.
EDIT: I solved the original problem (std::tuple<...> -> std::vector<std::type_index>). I can't imagine another context at the moment but maybe the fundamental question still stands.
I did it by using a trick involving expanding a parameter pack over the typeid function in the constructor of std::vector (or std::array).
template<class... Args>
std::array<std::type_index, sizeof...(Args)> const& types_info<std::tuple<Args...>>::value{typeid(Args)...};
The complete code is this (note that I also decided to use std::array).
#include <typeindex>
#include<array>
#include<iostream>
template<class T>
struct types_info;
template<class... Args>
struct types_info<std::tuple<Args...>>{
static std::array<std::type_index, sizeof...(Args)> const& value;//{typeid(Args)...};
};
template<class... Args>
std::array<std::type_index, sizeof...(Args)> const& types_info<std::tuple<Args...>>::value{typeid(Args)...};
// vvv works only in C++1z
template<template<typename...> typename T, class... Args> // non tuples types as well
struct types_info<T<Args...>> : types_info<std::tuple<Args...>>{};
using tuple_type = std::tuple<std::string&, int>;
int main(){
std::vector<std::type_index> types;
std::cout << types_info<tuple_type>::value.size() << std::endl;
std::cout << types_info<std::map<int, std::string>>::value.size() << std::endl;
}
I tried the following code but it gives:
main.cpp:29:22: error: aggregate 'pop<std::tuple<int, char, float> > p' has incomplete type and cannot be defined
What am I missing?
template <typename T>
struct pop;
template <typename E, typename... Ts>
struct pop<tuple<Ts..., E>> {
using result = tuple<Ts...>;
};
tuple<int, char, float> t;
typename pop<decltype(t)>::result p;
If Ts... must be at the end in a type list, why does it work in this example from http://en.cppreference.com/w/cpp/language/parameter_pack:
template<class A, class B, class...C> void func(A arg1, B arg2, C...arg3)
{
container<A,B,C...> t1; // expands to container<A,B,E1,E2,E3>
container<C...,A,B> t2; // expands to container<E1,E2,E3,A,B>
container<A,C...,B> t3; // expands to container<A,E1,E2,E3,B>
}
tuple<Ts..., E> is a non-deduced context. [temp.deduct.type]/9:
If P has a form that contains <T> or <i>, then each argument Pi of the respective template argument list P is compared with the corresponding argument Ai of the corresponding template argument list of A. If the template argument list of P contains a pack expansion that is not the last template argument, the entire template argument list is a non-deduced context.
That means that your partial specialization is never matched.
With C++14, one could use
template <class T, class=std::make_index_sequence<std::tuple_size<T>::value-1>>
struct pop;
template <typename Tuple, std::size_t... indices>
struct pop<Tuple, std::index_sequence<indices...>>
{
using type = std::tuple<std::tuple_element_t<indices, Tuple>...>;
};
template <typename T>
using pop_t = typename pop<T>::type;
Such that
using t = std::tuple<int, char, float>;
static_assert( std::is_same<pop_t<t>, std::tuple<int, char>>{}, "" );
Compiles.
Demo.
Ts... must be the last element of a type list if you want it to be deduced. tuple<Ts...,E> will not deduce Ts... to be all but the last, but rather never match anything.
Getting rid of the last argument is a bit tricker. live example:
#include <iostream>
#include <tuple>
#include <iostream>
namespace details {
template<class Lhs, class Rhs>
struct pop_helper;
template<template<class...>class Tup, class L0, class...Lhs, class...Rhs>
struct pop_helper<Tup<L0,Lhs...>, Tup<Rhs...>>:
pop_helper<Tup<Lhs...>, Tup<Rhs...,L0>>
{};
template<template<class...>class Tup, class L0, class...Rhs>
struct pop_helper<Tup<L0>, Tup<Rhs...>> {
using type=Tup<Rhs...>;
};
}
template <typename T>
struct pop;
template<template<class...>class Tup, class...Ts>
struct pop<Tup<Ts...>>:
details::pop_helper<Tup<Ts...>,Tup<>>
{};
template<typename T>
using pop_t=typename pop<T>::type;
std::tuple<int, char, float> t;
typedef pop_t<decltype(t)> p;
int main() {
p x = std::make_tuple( 7, 'x' );
std::cout << std::get<0>(x) << std::get<1>(x) << std::tuple_size<p>{} << "\n";
}
pop_helper moves the types over one at a time to the right hand side, until there is only one type left on the left hand side. Then it returns the right hand side type.
pop just passes the tuples over.
I used template<class...>class Tup instead of std::tuple, because why not support almost every template instead of just std::tuple?
pop_t gets rid of the annoying typename spam at point of use.
I use the inhertance-as-type-map-forwarding pattern, which saves on typing. With a type-map, the structure:
template<class X>
struct bob: foo<X> {};
can be read as bob<X> is foo<X>. The alternative is the more verbose
template<class X>
struct bob {
using type = typename foo<X>::type;
};
expanding variardic type lists is different than matching them. When it was designed, matching was kept simple in order to make compiler vendors be able to implement the feature. There may even be thorny issues beyond "it is tricky" down that path.
Another C++11 way to skin this cat:
#include <tuple>
template<class Tuple>
struct pop;
template<>
struct pop<std::tuple<>>
{
using type = std::tuple<>;
};
template<typename T1>
struct pop<std::tuple<T1>>
{
using type = std::tuple<>;
};
template<typename First, typename... Rest>
struct pop<std::tuple<First,Rest...>>
{
using type =
decltype(std::tuple_cat(
std::declval<std::tuple<First>>(),
std::declval<typename pop<std::tuple<Rest...>>::type>()));
};
// Test...
static_assert(std::is_same<pop<std::tuple<>>::type,std::tuple<>>::value,"");
static_assert(std::is_same<pop<std::tuple<int>>::type,std::tuple<>>::value,"");
static_assert(
std::is_same<pop<std::tuple<int,char>>::type,std::tuple<int>>::value,"");
static_assert(
std::is_same<pop<std::tuple<int,char,float>>::type,
std::tuple<int,char>>::value,"");
static_assert(
std::is_same<pop<std::tuple<int,char,float,double>>::type,
std::tuple<int,char,float>>::value,"");
This is the solution I had come up with:
template <typename T>
struct pop;
template <typename E, typename... Ts>
struct pop<std::tuple<E, Ts...>> {
using type = decltype(tuple_cat(
declval<tuple<E>>(),
declval<typename pop<tuple<Ts...>>::type>()
));
};
template <typename E>
struct pop<std::tuple<E>> {
using type = std::tuple<>;
};
I'm experimenting with C++11 (I've used old C++ so far) and I wrote the following code:
#include <iostream>
#include <vector>
#include <type_traits>
using namespace std;
constexpr bool all_true(){
return true;
}
template <typename Head, typename... Tail>
constexpr bool all_true(Head head, Tail... tail){
static_assert( is_convertible<bool, Head>::value, "all_true arguments must be convertible to bool!");
return static_cast<bool>(head) && all_true(tail...);
}
template<typename T, typename... Args>
void print_as(Args... args){
static_assert( all_true(is_convertible<T,Args>::value...), "all arguments must be convertible to the specified type!");
vector<T> v {static_cast<T>(args)...};
for(T i : v) cout << i << endl;
}
int main(){
print_as<bool>(1, 2, 0, 4.1);
}
The code compiles and runs as expected (I used gcc 4.6). I would like to aks the following questions:
I initialized a std::vector with an expanded parameter pack ( vector v {static_cast(args)...}; ). Is this correct C++11? I haven't found this feature explained anywhere.
I don't like too much the declaration of all_true because I know the type but I use templates. Is it possible to use something similar to the following?
constexpr bool all_true(bool head, bool... tail){...} // This code doesn't compile
Thanks!
Yes, it is possible to use pack expansions inside initialiser lists. C++11 [temp.variadic]§4 allows this:
... Pack expansions can occur in the following contexts:
...
In an initializer-list (8.5); the pattern is an initializer-clause.
No, there's no way to make a non-template typesafe variadic function. What you have is OK. There was a question about this recently.