2016-05-20 2 views
17

Ist es möglich, den Typ der Argumente in einem Variadic-Konstruktor zu beschränken?Wie kann ich einen C++ - Konstruktor erstellen, der eine variable Zahl von int akzeptiert

Ich möchte in der Lage sein, zum Ausdruck bringen

X x1(1,3,4); 
X x2(3,4,5); 

// syntax error: identifier 'Args' 
class X { 
template<int ... Args> X(Args...) 
{ 
} 
}; 
// this works but allows other types than int 
class Y { 
template<typename ... Args> Y(Args...) 
{ 
} 
}; 

bearbeiten Absicht zu klären:

Was ich erreichen möchte ist, Daten in einen Konstruktor übergeben zu speichern (Konstanten bei der Kompilierung bekannt) in ein statisches Array.

, so gibt es einige andere

template<int ...values> 
struct Z 
{ 
    static int data[sizeof...(values)]; 
}; 

template<int ... values> 
int Z<values...>::data[sizeof...(values)] = {values...}; 

und im Konstruktor von X würde ich Z wie diese verwenden möchten:

class X { 
    template<int ... Args> X(Args...) 
    { 
     Z<Args...>::data // do stuff with data 
    } 
}; 

Ist das möglich, unser muss ich integer_sequence benutzen?

Antwort

15

Da haben Sie die folgenden Schritte aus:

template<int... values> 
struct Z 
{ 
    static int data[ sizeof...(values) ]; 
}; 

template <int... values> 
int Z<values...>::data[ sizeof...(values) ] = { values... }; 

Sie können std::integer_sequence<> verwenden, um Z<> in den Ints passieren:

struct X 
{ 
    template <int... values> 
    X(std::integer_sequence<int, values...>) 
    { 
     for (int i{ 0 }; i < sizeof...(values); ++i) 
      Z<values...>::data[ i ]; // do stuff with data 
    } 
}; 

Sie können sich einen Helfer-Typ machen, um es zu nennen leicht zu machen, die ctor:

template <int... values> 
using int_sequence = std::integer_sequence<int, values...>; 

Dann können Sie Ihre Klasse lik instanziieren so e:

int main() 
{ 
    X x(int_sequence<1, 3, 5>{}); 
} 
+1

Sind Sie sicher, dass OPs 'Kompilierzeitkonstanten sind? – Barry

+0

@Barry - Ja, sie sind zur Kompilierzeit bekannt! –

+0

@StaffanGustafsson - Das wäre nützliche Informationen in Ihrer Frage gewesen. –

-5

Nein, Sie können den Typ nicht einschränken. Sie können jedoch static_assert verwenden. Wäre so etwas wie dieses:

static_assert(std::is_same<int, Args>::value ..., "have to be ints."); 

nicht haben versucht, eine Expansion in einem static_assert wie dass, obwohl zu verwenden. Möglicherweise benötigen Sie einen constexpr, der bool oder etwas zurückgibt.

+0

Ja, Sie können den Typ einschränken. Und nein, du kannst die Packexpansion nicht in einer 'statischen_Auswahl 'wie dieser haben. – Barry

14

können Sie std::initializer_list verwenden:

#include <iostream> 
#include <initializer_list> 

void myFunc(std::initializer_list<int> args) 
{ 
    for (int i: args) std::cout << i << '\n'; 
} 
int main(){ 

    myFunc({2,3,2}); 
    // myFunc({2,"aaa",2}); error! 

} 
1

Sie haben Ihre Frage aktualisiert, um anzuzeigen, dass alles, was Sie brauchen, ist eine Kompilierung-std::integer_sequence, das ist toll.

Aber nur für zukünftige Leser, die hier nach der Antwort suchen könnten, möchte ich auch Ihre ursprüngliche Frage beantworten "Ist es möglich, die Art der Argumente in einem variadic Konstruktor zu beschränken?"

Ja. Eine Möglichkeit (? Der beste Weg, ich bin mir nicht sicher) ist SFINAE auf einem zusätzlichen Template-Parameter, wie folgt aus:

struct X { 
    template< 
     class... TT, 
     class E = std::enable_if_t<(std::is_same_v<TT, int> && ...)> 
    > 
    X(TT... tt) { 
     // do stuff with the ints "tt..." 
    } 
}; 

Die && ... ist ein Klapp Ausdruck, neu in C++ 17. Wenn dein Compiler keine falten Ausdrücke unterstützt, ersetze das einfach mit einem handgerollten all_of.

Verwandte Themen