char and initializer lists
Asked Answered
P

3

2

I'd like to pass some numeric byte values via an initializer list a variadic template into an array. Is that possible?

template < int N > struct a {
  char s[N];
  template < typename ... A >
  a (A ... _a) : s {_a...} {}
};

int main () {
  // g++-4.5: error: narrowing conversion of »_a#0« from »int« to »char« inside { }
  a < 3 > x { 1, 2, 3 };
}

What I can think of is

  • to use octal representation, '\001' etc., or
  • to cast every single value.

But both is not satisfying.

Patella answered 15/9, 2010 at 16:46 Comment(2)
Why is it a problem to cast the values? If you want to be careful about the kind of cast that happens, use boost::numeric_cast and specifically allow narrowing conversions. One way or another, you are narrowing those arguments.Smile
If you don't provide any constructor, you'll be able to use brace initialization for aggregates.Precedence
S
0

NOTE: All of this is unnecessary unless you have added functionality to the class so it's no longer an aggregate. (For example, other constructors, private members, a base class, etc.) The immediate way to fix the code in the question is simply to remove the constructor. So, let's assume there's something more to it.

I've seen some people trying to do things like this. It seems ugly, dealing with conversion semantics and trying to artificially re-create the functionality of a usual function call.

Here is a strategy to create an array class that simply has the right constructor in the first place.

Template aliasing would put the icing on the cake by hiding the ::type ugliness, but it's not in GCC yet.

template< typename ... NT >
struct var_ctor_array {
    enum { size_e = 0 }; // only used for zero size case
};

template< typename T, typename ... NT >
struct var_ctor_array< T, NT ... > {
    enum { size_e = 1 + sizeof...( NT ) };

    T st[ size_e ];

    var_ctor_array( T elem0, NT ... elemN )
        : st { elem0, elemN ... } {}
};

template< typename T, size_t N, typename ... NT >
struct gen_var_ctor_array {
    typedef typename gen_var_ctor_array< T, N-1, T, NT ... >::type type;
};

template< typename T, typename ... NT >
struct gen_var_ctor_array< T, 0, NT ... > {
    typedef var_ctor_array< NT ... > type;
};

int main() { // usage
    gen_var_ctor_array< char, 5 >::type five( 1, 2, 3, 4, 5 );
}
Smile answered 16/9, 2010 at 4:59 Comment(5)
Awesome idea, I'll use the classes. g++ won't compile without the special case for size_e == 0 due to a bug. Now, would it be possible to have T() as default value for every parameter?Patella
@Thomas: Trying it out, "parameter pack ‘elemN’ cannot have a default argument". However, that feature could be hacked using inheriting constructors, which aren't in GCC 4.5.Smile
What triggers that bug? gen_var_ctor_array< char, 0 >::type zero; works…Smile
I meant: Leaving away the first four lines (because I won't have arrays of size 0) and removing the template list in line 7 would cause g++ to fail. (But of course, adding the special case is better, anyway).Patella
@Thomas: Ah, yes, that's an integral part. The comment "only used" is just referring to the one line, inside the braces.Smile
C
2

You don't need any complicated code

template < int N > struct a {
  char s[N];
  template < typename ... A >
  a (A ... _a) : s {static_cast<char>(_a)...} {}
};
Cigar answered 14/1, 2011 at 12:31 Comment(0)
T
0

You're not actually using initializer lists. The constructor receives a variadic template and you initialize x with uniform initialization.

The only problem is I don't know of an elegant way of initializing an array with an initializer_list, AFAIK std::array should have a constructor that accepts initializer_list but it doesn't seem to be supported by g++ yet.

#include <utility>
template < int N > struct a {
    char s[N];

    a (std::initializer_list<char> list) {
        if (N != list.size()) 
            throw "list wrong size";

        int i = 0;
        const char* p = list.begin();
        while(p != list.end())
            s[i++] = *p++;
    }
};
Ticknor answered 15/9, 2010 at 19:0 Comment(2)
You're absolutely right, variadic templates is what I meant to write. Initializer lists are perhaps not as efficient for array initialization.Patella
array doesn't need to support initializer_list because it already does the same thing in C++03 by being an aggregate. Nothing is more efficient than that, as it amounts to in-place memberwise initialization.Smile
S
0

NOTE: All of this is unnecessary unless you have added functionality to the class so it's no longer an aggregate. (For example, other constructors, private members, a base class, etc.) The immediate way to fix the code in the question is simply to remove the constructor. So, let's assume there's something more to it.

I've seen some people trying to do things like this. It seems ugly, dealing with conversion semantics and trying to artificially re-create the functionality of a usual function call.

Here is a strategy to create an array class that simply has the right constructor in the first place.

Template aliasing would put the icing on the cake by hiding the ::type ugliness, but it's not in GCC yet.

template< typename ... NT >
struct var_ctor_array {
    enum { size_e = 0 }; // only used for zero size case
};

template< typename T, typename ... NT >
struct var_ctor_array< T, NT ... > {
    enum { size_e = 1 + sizeof...( NT ) };

    T st[ size_e ];

    var_ctor_array( T elem0, NT ... elemN )
        : st { elem0, elemN ... } {}
};

template< typename T, size_t N, typename ... NT >
struct gen_var_ctor_array {
    typedef typename gen_var_ctor_array< T, N-1, T, NT ... >::type type;
};

template< typename T, typename ... NT >
struct gen_var_ctor_array< T, 0, NT ... > {
    typedef var_ctor_array< NT ... > type;
};

int main() { // usage
    gen_var_ctor_array< char, 5 >::type five( 1, 2, 3, 4, 5 );
}
Smile answered 16/9, 2010 at 4:59 Comment(5)
Awesome idea, I'll use the classes. g++ won't compile without the special case for size_e == 0 due to a bug. Now, would it be possible to have T() as default value for every parameter?Patella
@Thomas: Trying it out, "parameter pack ‘elemN’ cannot have a default argument". However, that feature could be hacked using inheriting constructors, which aren't in GCC 4.5.Smile
What triggers that bug? gen_var_ctor_array< char, 0 >::type zero; works…Smile
I meant: Leaving away the first four lines (because I won't have arrays of size 0) and removing the template list in line 7 would cause g++ to fail. (But of course, adding the special case is better, anyway).Patella
@Thomas: Ah, yes, that's an integral part. The comment "only used" is just referring to the one line, inside the braces.Smile

© 2022 - 2024 — McMap. All rights reserved.