9

C++11 standard have std::conditional<> template for the type selection by the some boolean condition at compiler time. How to do the same operation but for select the init value for variable initialization? Similar to type a = (exp) ? first_value : second_value;.

I use my template:

template<bool B, typename T>
inline constexpr T&& conditional_initialize(T&& i1, T&& i2) {
    return B ? std::move(i1) : std::move(i2);
}

But it can be used only for POD types: int a = conditional_initialize<true>(1, 2);. For array initialization this template is compiled with error. Wrong compile example: int a[] = conditional_initialize<true>({1, 2}, {3,4,5});

Error message: no matching function for call to 'conditional_initialize(<brace-enclosed initializer list>, <brace-enclosed initializer list>)';

Who can help me with template?

Miles Budnek
  • 28,216
  • 2
  • 35
  • 52
23W
  • 1,413
  • 18
  • 37
  • 2
    @AndyG No, it's `std::initializer_list` – alexeykuzmin0 Aug 08 '16 at 14:50
  • `int a[] = conditional_initialize({1, 2}, {3,4,5});` This cannot work, you cannot copy or move construct built-in arrays. – Baum mit Augen Aug 08 '16 at 14:53
  • 2
    @alexeykuzmin0 it would be indeed, but unfortunately [templates cannot deduce `std::initializer_list`](http://stackoverflow.com/a/12431810/3233393). – Quentin Aug 08 '16 at 14:55
  • 2
    First, `int[]` is a POD type. Second you cannot initialize an `int a[]` from an `initializer_list`, which is pretty much the best thing you can hope `{1,2}` is deduced to be in that call. `{}` constructs are not expressions, so they cannot be stored perfectly in a typed variable or parameter. They can be use to construct typed storage, but that typed storage is not the same thing as the `{}`. This is one of the failings of "perfect" forwarding. – Yakk - Adam Nevraumont Aug 08 '16 at 14:56
  • @Quentin They can, it just takes work, and that works isn't done, nor is it sufficient (add an overload that takes `(std::initializer_list i1, std::initializer_list i2)` for example). – Yakk - Adam Nevraumont Aug 08 '16 at 14:56
  • Yakk, this sample not works too - http://cpp.sh/8aain – 23W Aug 08 '16 at 15:05
  • 1
    @23W because as he stated, you can't initialize a built-in array with an `std::initializer_list`. For one thing, the built-in array needs to know its length at compile time and the `std::initializer_list` hides that information until run time. – jaggedSpire Aug 08 '16 at 15:06

1 Answers1

8
template<class T, std::size_t N, std::size_t M, bool b>
std::array<T, b?N:M>
conditional_array( std::array<T, N>&& lhs, std::array<T, M>&& rhs ) {
  return std::move(std::get<b?0:1>( std::tie(lhs, rhs) ) );
}

this gives you:

auto a = conditional_array<int,2,3,true>({{1, 2}}, {{3,4,5}});

which is close.

In general, {} constructs are not expressions, they cannot be perfectly forwarded through any mechanism through another variable.

We can also get:

auto a = cond_init<true>( make_array(1,2), make_array(3,4,5) );

with a bit more work.

template<bool Test, class A, class B>
std::conditional_t<Test,A,B>
cond_init(A a, B b) {
  return std::move( std::get<Test?0:1>( std::tie(a,b) ) );
}
template<class T0, class...Ts>
std::array< std::decay_t<T0>, sizeof...(Ts)+1 >
make_array( T0&& t0, Ts&&...ts ) {
  return {{std::forward<T0>(t0), std::forward<Ts>(ts)...}};
}

I didn't make these constexpr, because lazy.

AndyG
  • 39,700
  • 8
  • 109
  • 143
Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • @23W isn't that just my `cond_init` and manual `make_array` case with small changes? I was lazy in that I did not audit of `std::tie` and the like are sufficiently `constexpr` for whatever version of C++ you used. – Yakk - Adam Nevraumont Aug 08 '16 at 16:25
  • Yes it is. It is modification of your code for single approach for simple types and c++ std array (which are different types for different sizes). – 23W Aug 08 '16 at 18:16