std::vector of type deduced from initializers before C++17 ... any workaround for C++11? - c++11

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.

Related

C++11, Is it possible to force an instance to be extern but also a constant expression of a non-type template parameter?

Using C++11, g++ (GCC) 4.4.7 20120313 (Red Hat 4.4.7-18).
Lets pretend I have a templated function (pardon my terminology if it isn't quite right).
I want to perform a "general" algorithm based on what was supposed to be compile-time instances of "field". Where the only things that really changed are these constants which I moved into trait classes (only added one here but imagine there are more). Originally I was declaring it as
constexpr field FIELD1{1};
However in C++11, non-type template params need to have external linkage (unlike C++14 which can have internal and external linkage?). So because not's in the same translation unit I needed to use extern in order to give it external linkage (sorry if I butchered that explanation also). But by defining it extern I can't define it using constexpr and it seems that losing that constexpr constructor this field is no longer a valid constant expression to qualify as a non-type template param.
Any suggestions if there is some way I can get around this? Open to a new method of doing things. Below is a simplified (incomplete, and non-compiling version to get the gist of the organization).
So the error I am seeing is along the lines of
error: the value of ‘FIELD1’ is not usable in a constant expression
note: ‘FIELD1’ was not declared ‘constexpr’
extern const field FIELD1;
Not quite sure what could be a best alternative.
I can get rid of the second error by removing the constexpr from the constructor. But then I don't know how to approach the constant expression issue.
field.H
struct field
{
int thingone;
constexpr field(int i):thingone(i){}
};
extern const field FIELD1;
field.C
#include "field.H"
const field FIELD1{0};
field_traits.H
#include "field.H"
template< const field& T >
class fieldTraits;
template< >
class fieldTraits<FIELD1>
{
public:
// Let's say I have common field names
// with different constants that I want to plug
// into the "function_name" algorithm
static constexpr size_t field_val = 1;
};
function.H
#include "field.H"
template< const field& T, typename TT = fieldTraits<T> >
void function_name()
{
// Let's pretend I'm doing something useful with that data
std::cout << T.thingone << std::endl;
std::cout << TT::field_val << std::endl;
}
So because not's in the same translation unit I needed to use extern in order to give it external linkage (sorry if I butchered that explanation also). But by defining it extern I can't define it using constexpr [...]
Per my comment, you can. It wouldn't work for you, but it's a step that helps in coming up with something that would work:
extern constexpr int i = 10;
This is perfectly valid, gives i external linkage, and makes i usable in constant expressions.
But it doesn't allow multiple definitions, so it can't work in a header file which is included in multiple translation units.
Ordinarily, the way around that is with inline:
extern inline constexpr int i = 10;
But variables cannot be declared inline in C++11.
Except... when they don't need to be declared inline because the effect has already been achieved implicitly:
struct S {
static constexpr int i = 10;
};
Now, S::i has external linkage and is usable in constant expressions!
You may not even need to define your own class for this, depending on the constant's type: consider std::integral_constant. You can write
using i = std::integral_constant<int, 10>;
and now i::value will do exactly what you want.

Passing an unspecialised template to another template

Given the following code here in IDEOne:
#include <iostream>
#include <vector>
#include <list>
template<typename T>
class MyVectorCollection
{
using collection = std::vector<T>;
};
template<typename C, typename T>
class MyGenericCollection
{
using collection = C;
};
template<typename C, typename T>
class MyMoreGenericCollection
{
using collection = C<T>;
};
int main() {
// your code goes here
MyVectorCollection<int> a;
MyGenericCollection<std::list<int>, int> b;
MyMoreGenericCollection<std::list, int> c; // How to do this?
return 0;
}
I get the error:
prog.cpp:20:24: error: ‘C’ is not a template
using collection = C<T>;
^
prog.cpp: In function ‘int main()’:
prog.cpp:27:43: error: type/value mismatch at argument 1 in template parameter list for ‘template<class C, class T> class MyMoreGenericCollection’
MyMoreGenericCollection<std::list, int> c;
^
prog.cpp:27:43: note: expected a type, got ‘list’
How can I write my code such that I can use a C<T> at compile time without having an explicit list of potential specialisations, and avoiding macros, if possible? I realise std::list isn't a typename, but I don't know how to progress, and I have been unable to find a similar question here.
(Note that this is just an MCVE, my actual usage is much more involved.)
Just to tidy up, here is the solution. The search term I was looking for is template template, and whilst I did find a possible duplicate, I think this question and answer is much simpler to follow.
So, thanks to the hint from #Some programmer dude, I looked up template template and updated the code to this, which does compile:
template<template<typename, typename> class C, typename T>
class MyMoreGenericCollection
{
using collection = C<T, std::allocator<T>>;
};
We declare the first template parameter as a template itself, and remembering that the standard library constructors take two parameters, we need to make the inner template take two parameters. There is (as far as I am aware) no way to automatically use the default second parameter, so for the sake of this example I explicitly state the default.
I could of course add a third parameter to the master template that could be used to specify the allocator, which itself would also be a template, but I leave that as an exercise for th reader.

Can Boost assert that a method call instantiation would not compile?

The following code works fine, I am happy with my robust class.
However, asserting that certain method call instantiations do not compile by manually commenting in and out when compiling, is, the least, very tedious. I understand that foreseeing that the compilation will break can only be achieved by actually compiling. This is a catch22 situation. But is there some beautiful way of getting around it without too much code obfuscation?
Does Boost have something to offer?
#include <tchar.h>
#include <type_traits>
template<int SOMEMEANINGFULNUMBER>
class RobustClass {
private:
RobustClass() {}
public:
static RobustClass<SOMEMEANINGFULNUMBER> instance;
template <int I = SOMEMEANINGFULNUMBER>
typename std::enable_if<(I != 2) && (I == SOMEMEANINGFULNUMBER)>::type
doSomething() {
// Do something smart and useful!
}
};
template<int SOMEMEANINGFULNUMBER>
RobustClass<SOMEMEANINGFULNUMBER> RobustClass<SOMEMEANINGFULNUMBER>::instance;
typedef RobustClass<0> RobustClass0;
typedef RobustClass<2> RobustClass2;
int _tmain(int argc, _TCHAR* argv[])
{
RobustClass<0> robustClass0 = RobustClass0::instance;
RobustClass<2> robustClass2 = RobustClass2::instance;
robustClass0.doSomething(); // Compiles and runs fine
// robustClass2.doSomething(); // Beautifully breaks compilation.
// But I want to wrap this call so
// that it does NOT break the compilation.
// May be a catch22 wish, however.
return 0;
}
I believe you want to be able to check that RobustClass<2>::doSomething() cannot be invoked from an instance of type RobustClass<2>.
Currently you check for this by forcing a build break, then commenting it out. This is annoying, because each check requires manually editing code.
We can detect if .doSomething() is valid to do on a type T using SFINAE, then invert the test, as follows:
namespace details {
template<template<class...>class Z, class, class...Ts>
struct can_apply:std::false_type{};
template<template<class...>class Z, class...Ts>
struct can_apply<Z, std::void_t<Z<Ts...>>, Ts...>:std::true_type{};
}
template<template<class...>class Z, class...Ts>
using can_apply = details::can_apply<Z, void, Ts...>;
template<class X>
using do_something_r = decltype( std::declval<X>().doSomething() );
template<class X>
using can_do_something = can_apply<do_something_r, X>;
This detects if X is a valid type to doSomething on it.
You can use the test like this:
static_assert( !can_do_something<RobustClass<2>&>{}, "doSomething should be disabled" );
can_apply is similar, but not identical, to the C++20 proposed is_detected.

Runtime iteration over tuple types without construction

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;
}

Can we use parameter packs as std::vector initializers?

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.

Resources