Parameter Pack with alternating types











up vote
8
down vote

favorite
2












I have a struct C which gets initialized with a variable number of instances of struct A and struct B. E.g.:



struct A
{};

struct B
{};

struct C
{
C(A&& o1, B&& p1, A&& o2)
{}
C(A&& o1, B&& p1, A&& o2, B&& p2, A&& o3)
{}
C(A&& o1, B&& p1, A&& o2, B&& p2, A&& o3, B&& p3, A&& o4)
{}
C(A&& o1, B&& p1, A&& o2, B&& p2, A&& o3, B&& p3, A&& o4, B&&p4, A&& o5)
{}
};


So, rather than providing multiple ctor's with different number of parameters I would like to find something generic.
However, the number of ctor parameters always grows about two parameters: B&& and A&&.
Could this be accomplished using parameter packs. Or would be another solution without implementing for each number of parameters an according ctor?



The goal should be that struct C can be constructed like the following examples:



C c1 = { A(), B(), A() };
C c2 = { A(), B(), A(), B(), A(), B(), A() };


etc.










share|improve this question









New contributor




PeWe is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
















  • 1




    Does the number of parameters need to be known at compile-time? In any case consider passing std::pair<A, B>&&s instead.
    – user10605163
    yesterday












  • During compile time would be the sugar on the top. However, the approach with std::pair had some problems (as I tried it): the expression looks a little weird C c = { A(), { B(), A() } }; and so far I wasn‘t able to hyve something txpesafe with a variable number of params.
    – PeWe
    yesterday












  • Actually I thought this initialization syntax is better, because it clearly shows which two arguments belong together. What do you mean by typesafe? You can make sure that the template parameters are as expected with static_assert or SFINAE if you need other overloaded constructors.
    – user10605163
    yesterday










  • Actually, the syntax would be more clear without curly braces in between. I‘m not very familiar with SFINAE, sounds I should learn more about it.
    – PeWe
    yesterday

















up vote
8
down vote

favorite
2












I have a struct C which gets initialized with a variable number of instances of struct A and struct B. E.g.:



struct A
{};

struct B
{};

struct C
{
C(A&& o1, B&& p1, A&& o2)
{}
C(A&& o1, B&& p1, A&& o2, B&& p2, A&& o3)
{}
C(A&& o1, B&& p1, A&& o2, B&& p2, A&& o3, B&& p3, A&& o4)
{}
C(A&& o1, B&& p1, A&& o2, B&& p2, A&& o3, B&& p3, A&& o4, B&&p4, A&& o5)
{}
};


So, rather than providing multiple ctor's with different number of parameters I would like to find something generic.
However, the number of ctor parameters always grows about two parameters: B&& and A&&.
Could this be accomplished using parameter packs. Or would be another solution without implementing for each number of parameters an according ctor?



The goal should be that struct C can be constructed like the following examples:



C c1 = { A(), B(), A() };
C c2 = { A(), B(), A(), B(), A(), B(), A() };


etc.










share|improve this question









New contributor




PeWe is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
















  • 1




    Does the number of parameters need to be known at compile-time? In any case consider passing std::pair<A, B>&&s instead.
    – user10605163
    yesterday












  • During compile time would be the sugar on the top. However, the approach with std::pair had some problems (as I tried it): the expression looks a little weird C c = { A(), { B(), A() } }; and so far I wasn‘t able to hyve something txpesafe with a variable number of params.
    – PeWe
    yesterday












  • Actually I thought this initialization syntax is better, because it clearly shows which two arguments belong together. What do you mean by typesafe? You can make sure that the template parameters are as expected with static_assert or SFINAE if you need other overloaded constructors.
    – user10605163
    yesterday










  • Actually, the syntax would be more clear without curly braces in between. I‘m not very familiar with SFINAE, sounds I should learn more about it.
    – PeWe
    yesterday















up vote
8
down vote

favorite
2









up vote
8
down vote

favorite
2






2





I have a struct C which gets initialized with a variable number of instances of struct A and struct B. E.g.:



struct A
{};

struct B
{};

struct C
{
C(A&& o1, B&& p1, A&& o2)
{}
C(A&& o1, B&& p1, A&& o2, B&& p2, A&& o3)
{}
C(A&& o1, B&& p1, A&& o2, B&& p2, A&& o3, B&& p3, A&& o4)
{}
C(A&& o1, B&& p1, A&& o2, B&& p2, A&& o3, B&& p3, A&& o4, B&&p4, A&& o5)
{}
};


So, rather than providing multiple ctor's with different number of parameters I would like to find something generic.
However, the number of ctor parameters always grows about two parameters: B&& and A&&.
Could this be accomplished using parameter packs. Or would be another solution without implementing for each number of parameters an according ctor?



The goal should be that struct C can be constructed like the following examples:



C c1 = { A(), B(), A() };
C c2 = { A(), B(), A(), B(), A(), B(), A() };


etc.










share|improve this question









New contributor




PeWe is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.











I have a struct C which gets initialized with a variable number of instances of struct A and struct B. E.g.:



struct A
{};

struct B
{};

struct C
{
C(A&& o1, B&& p1, A&& o2)
{}
C(A&& o1, B&& p1, A&& o2, B&& p2, A&& o3)
{}
C(A&& o1, B&& p1, A&& o2, B&& p2, A&& o3, B&& p3, A&& o4)
{}
C(A&& o1, B&& p1, A&& o2, B&& p2, A&& o3, B&& p3, A&& o4, B&&p4, A&& o5)
{}
};


So, rather than providing multiple ctor's with different number of parameters I would like to find something generic.
However, the number of ctor parameters always grows about two parameters: B&& and A&&.
Could this be accomplished using parameter packs. Or would be another solution without implementing for each number of parameters an according ctor?



The goal should be that struct C can be constructed like the following examples:



C c1 = { A(), B(), A() };
C c2 = { A(), B(), A(), B(), A(), B(), A() };


etc.







c++ templates parameters variadic-templates pack






share|improve this question









New contributor




PeWe is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.











share|improve this question









New contributor




PeWe is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.









share|improve this question




share|improve this question








edited yesterday









max66

33.8k63762




33.8k63762






New contributor




PeWe is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.









asked yesterday









PeWe

441




441




New contributor




PeWe is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.





New contributor





PeWe is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.






PeWe is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.








  • 1




    Does the number of parameters need to be known at compile-time? In any case consider passing std::pair<A, B>&&s instead.
    – user10605163
    yesterday












  • During compile time would be the sugar on the top. However, the approach with std::pair had some problems (as I tried it): the expression looks a little weird C c = { A(), { B(), A() } }; and so far I wasn‘t able to hyve something txpesafe with a variable number of params.
    – PeWe
    yesterday












  • Actually I thought this initialization syntax is better, because it clearly shows which two arguments belong together. What do you mean by typesafe? You can make sure that the template parameters are as expected with static_assert or SFINAE if you need other overloaded constructors.
    – user10605163
    yesterday










  • Actually, the syntax would be more clear without curly braces in between. I‘m not very familiar with SFINAE, sounds I should learn more about it.
    – PeWe
    yesterday
















  • 1




    Does the number of parameters need to be known at compile-time? In any case consider passing std::pair<A, B>&&s instead.
    – user10605163
    yesterday












  • During compile time would be the sugar on the top. However, the approach with std::pair had some problems (as I tried it): the expression looks a little weird C c = { A(), { B(), A() } }; and so far I wasn‘t able to hyve something txpesafe with a variable number of params.
    – PeWe
    yesterday












  • Actually I thought this initialization syntax is better, because it clearly shows which two arguments belong together. What do you mean by typesafe? You can make sure that the template parameters are as expected with static_assert or SFINAE if you need other overloaded constructors.
    – user10605163
    yesterday










  • Actually, the syntax would be more clear without curly braces in between. I‘m not very familiar with SFINAE, sounds I should learn more about it.
    – PeWe
    yesterday










1




1




Does the number of parameters need to be known at compile-time? In any case consider passing std::pair<A, B>&&s instead.
– user10605163
yesterday






Does the number of parameters need to be known at compile-time? In any case consider passing std::pair<A, B>&&s instead.
– user10605163
yesterday














During compile time would be the sugar on the top. However, the approach with std::pair had some problems (as I tried it): the expression looks a little weird C c = { A(), { B(), A() } }; and so far I wasn‘t able to hyve something txpesafe with a variable number of params.
– PeWe
yesterday






During compile time would be the sugar on the top. However, the approach with std::pair had some problems (as I tried it): the expression looks a little weird C c = { A(), { B(), A() } }; and so far I wasn‘t able to hyve something txpesafe with a variable number of params.
– PeWe
yesterday














Actually I thought this initialization syntax is better, because it clearly shows which two arguments belong together. What do you mean by typesafe? You can make sure that the template parameters are as expected with static_assert or SFINAE if you need other overloaded constructors.
– user10605163
yesterday




Actually I thought this initialization syntax is better, because it clearly shows which two arguments belong together. What do you mean by typesafe? You can make sure that the template parameters are as expected with static_assert or SFINAE if you need other overloaded constructors.
– user10605163
yesterday












Actually, the syntax would be more clear without curly braces in between. I‘m not very familiar with SFINAE, sounds I should learn more about it.
– PeWe
yesterday






Actually, the syntax would be more clear without curly braces in between. I‘m not very familiar with SFINAE, sounds I should learn more about it.
– PeWe
yesterday














3 Answers
3






active

oldest

votes

















up vote
6
down vote













You can use a variadic template and SFINAE to enable only the constructor where the type parameters satisfy your (or any arbitrary) condition.



#include <type_traits>
struct A {};
struct B {};


You need type_traits for std::false_type and std::true_type.



The alternates template is the key. The goal is to make alternates<X, Y, T1, T2, T3, ..., Tn> inherit from std::true_type if and only if the T1, ... Tn list is alternating X and Y. The default choice (just below) is no, but we specialize for matching cases.



template <typename X, typename Y, typename... Ts>
struct alternates : std::false_type {};


I choose to make this template more generic than your requirement here and allow alternates<X, Y> to inherit from true_type. The empty list satisfies the mathematical requirement that all elements of it alternate. This will be a good stopgap for the recursive definition below.



template <typename X, typename Y>
struct alternates<X, Y> : std::true_type {};


Any other list of alternates<X, Y, Ts...> alternates if and only if Ts... minus the first element alternate Y and X (Y first!).



template <typename X, typename Y, typename... Ts>
struct alternates<X, Y, X, Ts...>
: alternates<Y, X, Ts...> {};

struct C
{


We define the constructor as a template that first takes a parameter pack (the type will be deduced, no need to specify when calling) and it has a defaulted template parameter for SFINAE purposes. If the defaulted argument cannot be calculated based on the parameter pack, the constructor will not exist. I added the extra conditions about the number of pairs which I assumed from the example.



    template<typename... Ts,
typename = typename std::enable_if<
sizeof...(Ts) % 2 == 1 &&
sizeof...(Ts) >= 3 && // did you imply this?
alternates<A, B, Ts...>::value
>::type>
C(Ts&&...);
};


The way SFINAE works is that std::enable_if only defines the std::enable_if<condition, T>::type (the ::type part) if condition is true. That can be any arbitrary boolean expression computable at compile time. If it is false, saying ::type at the end will be a substitution failure and the overload where you tried to use it (e.g., C{A(), A(), A()}) will simply not be defined.



You can test that the examples below work as expected. The ones commented out are not expected to work.



int main() {
C c1 { A(), B(), A() };
C c2 { A(), B(), A(), B(), A(), B(), A() };
// C c3 {}; // I assumed you need at least 2
// C c4 { A(), B(), A(), A() }; // A, A doesn't alternate
// C c5 { B(), A(), B() }; // B, A, B not allowed
// C c6 { A(), B(), A(), B() }; // A, B, A, B doesn't pair
}


Try the code here.






share|improve this answer



















  • 1




    I like this answer. One thing you can improve is that you don't need std::conditional: template <typename X, typename Y, typename... Ts> struct alternates<X, Y, X, Ts...> : alternates<Y, X, Ts...> {};
    – AndyG
    yesterday










  • I can't wait for compile-time reflection so that we can write type checks like this without using all these unreadable partial specialization-based techniques.
    – Brian
    yesterday










  • @AndyG thanks, edited.
    – palotasb
    yesterday










  • @Brian Might that also result in nicer error messages if I make the effort? I especially didn't like the error message of C c = {}: could not convert '<brace-enclosed initializer list>()' from '<brace-enclosed initializer list>' to 'C'
    – palotasb
    yesterday










  • @palotasb: You could write another ctor that has a !enable_if and then fails a static_assert on the interior
    – AndyG
    yesterday


















up vote
5
down vote













I suppose you can use a template delegating constructor



Something as follows



#include <utility>

struct A {};
struct B {};

struct C
{
C (A &&)
{ }

template <typename ... Ts>
C (A &&, B &&, Ts && ... ts) : C(std::forward<Ts>(ts)...)
{ }
};

int main()
{
C(A{});
C(A{}, B{}, A{});
C(A{}, B{}, A{}, B{}, A{});
C(A{}, B{}, A{}, B{}, A{}, B{}, A{});
}


If you require at least three element (so no C(A{}) but at least C(A{}, B{}, A{})) the not-template constructor become



   C (A &&, B &&, A&&)
{ }





share|improve this answer





















  • Well, the hard error can be irritating.
    – Deduplicator
    yesterday


















up vote
0
down vote













Maybe something like this would help...



#include <iostream>
#include <vector>
#include <utility>

// Simple classes A & B to represent your alternating pattern classes.
class A{ public: int a; };
class B{ public: int b; };

// helper class template to act as a single parameter kind of like std::pair...
template<typename T, typename U>
class Pack{
private:
T t_;
U u_;

public:
Pack( T&& t, U&& u ) :
t_( std::move( t ) ),
u_( std::move( u ) )
{}

T getT() const { return t_; }
U getU() const { return u_; }
};

// your class with varying amount of parameters for its ctors
template<class T, class U>
class C{
private:
std::vector<Pack<T,U>> packs_;

public:
template<typename... Packs>
C( Packs&&... packs ) : packs_{ std::move( packs )... } { }

std::vector<Pack<T,U>> getPacks() const {
return packs_;
}
};

// A few overloaded ostream operator<<()s for easy printing...
std::ostream& operator<<( std::ostream& os, const A& a ) {
os << a.a;
return os;
}

std::ostream& operator<<( std::ostream& os, const B& b ) {
os << b.b;
return os;
}

template<typename T, typename U>
std::ostream& operator<<( std::ostream& os, const Pack<T,U>& pack ) {
os << pack.getT() << " " << pack.getU() << 'n';
return os;
}

// Main program to demonstrate its use
int main() {
Pack<int,double> p1( 1, 2.3 ), p2( 4, 9.2 ), p3( 5, 3.5 );
C<int, double> c( p1, p2, p3 );
for (auto& p : c.getPacks() )
std::cout << p;

std::cout << 'n';

Pack<float, char> p4( 3.14f, 'a' ), p5( 6.95f, 'b' ),
p6( 2.81f, 'c' ), p7( 8.22f, 'd' );
C<float, char> c2( p4, p5, p6, p7 );
for ( auto& p : c2.getPacks() )
std::cout << p;

return 0;
}


Working Code



-Output-



1 2.3
4 9.2
5 3.5

3.14 a
6.95 b
2.81 c
8.22 d




-Note- I did not incorporate for any odd number of parameters. For a more detailed solution with odd cases you can refer to the other answers with SFINAE or Delegating Constructor.






share|improve this answer























    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });






    PeWe is a new contributor. Be nice, and check out our Code of Conduct.










    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53729854%2fparameter-pack-with-alternating-types%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    3 Answers
    3






    active

    oldest

    votes








    3 Answers
    3






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes








    up vote
    6
    down vote













    You can use a variadic template and SFINAE to enable only the constructor where the type parameters satisfy your (or any arbitrary) condition.



    #include <type_traits>
    struct A {};
    struct B {};


    You need type_traits for std::false_type and std::true_type.



    The alternates template is the key. The goal is to make alternates<X, Y, T1, T2, T3, ..., Tn> inherit from std::true_type if and only if the T1, ... Tn list is alternating X and Y. The default choice (just below) is no, but we specialize for matching cases.



    template <typename X, typename Y, typename... Ts>
    struct alternates : std::false_type {};


    I choose to make this template more generic than your requirement here and allow alternates<X, Y> to inherit from true_type. The empty list satisfies the mathematical requirement that all elements of it alternate. This will be a good stopgap for the recursive definition below.



    template <typename X, typename Y>
    struct alternates<X, Y> : std::true_type {};


    Any other list of alternates<X, Y, Ts...> alternates if and only if Ts... minus the first element alternate Y and X (Y first!).



    template <typename X, typename Y, typename... Ts>
    struct alternates<X, Y, X, Ts...>
    : alternates<Y, X, Ts...> {};

    struct C
    {


    We define the constructor as a template that first takes a parameter pack (the type will be deduced, no need to specify when calling) and it has a defaulted template parameter for SFINAE purposes. If the defaulted argument cannot be calculated based on the parameter pack, the constructor will not exist. I added the extra conditions about the number of pairs which I assumed from the example.



        template<typename... Ts,
    typename = typename std::enable_if<
    sizeof...(Ts) % 2 == 1 &&
    sizeof...(Ts) >= 3 && // did you imply this?
    alternates<A, B, Ts...>::value
    >::type>
    C(Ts&&...);
    };


    The way SFINAE works is that std::enable_if only defines the std::enable_if<condition, T>::type (the ::type part) if condition is true. That can be any arbitrary boolean expression computable at compile time. If it is false, saying ::type at the end will be a substitution failure and the overload where you tried to use it (e.g., C{A(), A(), A()}) will simply not be defined.



    You can test that the examples below work as expected. The ones commented out are not expected to work.



    int main() {
    C c1 { A(), B(), A() };
    C c2 { A(), B(), A(), B(), A(), B(), A() };
    // C c3 {}; // I assumed you need at least 2
    // C c4 { A(), B(), A(), A() }; // A, A doesn't alternate
    // C c5 { B(), A(), B() }; // B, A, B not allowed
    // C c6 { A(), B(), A(), B() }; // A, B, A, B doesn't pair
    }


    Try the code here.






    share|improve this answer



















    • 1




      I like this answer. One thing you can improve is that you don't need std::conditional: template <typename X, typename Y, typename... Ts> struct alternates<X, Y, X, Ts...> : alternates<Y, X, Ts...> {};
      – AndyG
      yesterday










    • I can't wait for compile-time reflection so that we can write type checks like this without using all these unreadable partial specialization-based techniques.
      – Brian
      yesterday










    • @AndyG thanks, edited.
      – palotasb
      yesterday










    • @Brian Might that also result in nicer error messages if I make the effort? I especially didn't like the error message of C c = {}: could not convert '<brace-enclosed initializer list>()' from '<brace-enclosed initializer list>' to 'C'
      – palotasb
      yesterday










    • @palotasb: You could write another ctor that has a !enable_if and then fails a static_assert on the interior
      – AndyG
      yesterday















    up vote
    6
    down vote













    You can use a variadic template and SFINAE to enable only the constructor where the type parameters satisfy your (or any arbitrary) condition.



    #include <type_traits>
    struct A {};
    struct B {};


    You need type_traits for std::false_type and std::true_type.



    The alternates template is the key. The goal is to make alternates<X, Y, T1, T2, T3, ..., Tn> inherit from std::true_type if and only if the T1, ... Tn list is alternating X and Y. The default choice (just below) is no, but we specialize for matching cases.



    template <typename X, typename Y, typename... Ts>
    struct alternates : std::false_type {};


    I choose to make this template more generic than your requirement here and allow alternates<X, Y> to inherit from true_type. The empty list satisfies the mathematical requirement that all elements of it alternate. This will be a good stopgap for the recursive definition below.



    template <typename X, typename Y>
    struct alternates<X, Y> : std::true_type {};


    Any other list of alternates<X, Y, Ts...> alternates if and only if Ts... minus the first element alternate Y and X (Y first!).



    template <typename X, typename Y, typename... Ts>
    struct alternates<X, Y, X, Ts...>
    : alternates<Y, X, Ts...> {};

    struct C
    {


    We define the constructor as a template that first takes a parameter pack (the type will be deduced, no need to specify when calling) and it has a defaulted template parameter for SFINAE purposes. If the defaulted argument cannot be calculated based on the parameter pack, the constructor will not exist. I added the extra conditions about the number of pairs which I assumed from the example.



        template<typename... Ts,
    typename = typename std::enable_if<
    sizeof...(Ts) % 2 == 1 &&
    sizeof...(Ts) >= 3 && // did you imply this?
    alternates<A, B, Ts...>::value
    >::type>
    C(Ts&&...);
    };


    The way SFINAE works is that std::enable_if only defines the std::enable_if<condition, T>::type (the ::type part) if condition is true. That can be any arbitrary boolean expression computable at compile time. If it is false, saying ::type at the end will be a substitution failure and the overload where you tried to use it (e.g., C{A(), A(), A()}) will simply not be defined.



    You can test that the examples below work as expected. The ones commented out are not expected to work.



    int main() {
    C c1 { A(), B(), A() };
    C c2 { A(), B(), A(), B(), A(), B(), A() };
    // C c3 {}; // I assumed you need at least 2
    // C c4 { A(), B(), A(), A() }; // A, A doesn't alternate
    // C c5 { B(), A(), B() }; // B, A, B not allowed
    // C c6 { A(), B(), A(), B() }; // A, B, A, B doesn't pair
    }


    Try the code here.






    share|improve this answer



















    • 1




      I like this answer. One thing you can improve is that you don't need std::conditional: template <typename X, typename Y, typename... Ts> struct alternates<X, Y, X, Ts...> : alternates<Y, X, Ts...> {};
      – AndyG
      yesterday










    • I can't wait for compile-time reflection so that we can write type checks like this without using all these unreadable partial specialization-based techniques.
      – Brian
      yesterday










    • @AndyG thanks, edited.
      – palotasb
      yesterday










    • @Brian Might that also result in nicer error messages if I make the effort? I especially didn't like the error message of C c = {}: could not convert '<brace-enclosed initializer list>()' from '<brace-enclosed initializer list>' to 'C'
      – palotasb
      yesterday










    • @palotasb: You could write another ctor that has a !enable_if and then fails a static_assert on the interior
      – AndyG
      yesterday













    up vote
    6
    down vote










    up vote
    6
    down vote









    You can use a variadic template and SFINAE to enable only the constructor where the type parameters satisfy your (or any arbitrary) condition.



    #include <type_traits>
    struct A {};
    struct B {};


    You need type_traits for std::false_type and std::true_type.



    The alternates template is the key. The goal is to make alternates<X, Y, T1, T2, T3, ..., Tn> inherit from std::true_type if and only if the T1, ... Tn list is alternating X and Y. The default choice (just below) is no, but we specialize for matching cases.



    template <typename X, typename Y, typename... Ts>
    struct alternates : std::false_type {};


    I choose to make this template more generic than your requirement here and allow alternates<X, Y> to inherit from true_type. The empty list satisfies the mathematical requirement that all elements of it alternate. This will be a good stopgap for the recursive definition below.



    template <typename X, typename Y>
    struct alternates<X, Y> : std::true_type {};


    Any other list of alternates<X, Y, Ts...> alternates if and only if Ts... minus the first element alternate Y and X (Y first!).



    template <typename X, typename Y, typename... Ts>
    struct alternates<X, Y, X, Ts...>
    : alternates<Y, X, Ts...> {};

    struct C
    {


    We define the constructor as a template that first takes a parameter pack (the type will be deduced, no need to specify when calling) and it has a defaulted template parameter for SFINAE purposes. If the defaulted argument cannot be calculated based on the parameter pack, the constructor will not exist. I added the extra conditions about the number of pairs which I assumed from the example.



        template<typename... Ts,
    typename = typename std::enable_if<
    sizeof...(Ts) % 2 == 1 &&
    sizeof...(Ts) >= 3 && // did you imply this?
    alternates<A, B, Ts...>::value
    >::type>
    C(Ts&&...);
    };


    The way SFINAE works is that std::enable_if only defines the std::enable_if<condition, T>::type (the ::type part) if condition is true. That can be any arbitrary boolean expression computable at compile time. If it is false, saying ::type at the end will be a substitution failure and the overload where you tried to use it (e.g., C{A(), A(), A()}) will simply not be defined.



    You can test that the examples below work as expected. The ones commented out are not expected to work.



    int main() {
    C c1 { A(), B(), A() };
    C c2 { A(), B(), A(), B(), A(), B(), A() };
    // C c3 {}; // I assumed you need at least 2
    // C c4 { A(), B(), A(), A() }; // A, A doesn't alternate
    // C c5 { B(), A(), B() }; // B, A, B not allowed
    // C c6 { A(), B(), A(), B() }; // A, B, A, B doesn't pair
    }


    Try the code here.






    share|improve this answer














    You can use a variadic template and SFINAE to enable only the constructor where the type parameters satisfy your (or any arbitrary) condition.



    #include <type_traits>
    struct A {};
    struct B {};


    You need type_traits for std::false_type and std::true_type.



    The alternates template is the key. The goal is to make alternates<X, Y, T1, T2, T3, ..., Tn> inherit from std::true_type if and only if the T1, ... Tn list is alternating X and Y. The default choice (just below) is no, but we specialize for matching cases.



    template <typename X, typename Y, typename... Ts>
    struct alternates : std::false_type {};


    I choose to make this template more generic than your requirement here and allow alternates<X, Y> to inherit from true_type. The empty list satisfies the mathematical requirement that all elements of it alternate. This will be a good stopgap for the recursive definition below.



    template <typename X, typename Y>
    struct alternates<X, Y> : std::true_type {};


    Any other list of alternates<X, Y, Ts...> alternates if and only if Ts... minus the first element alternate Y and X (Y first!).



    template <typename X, typename Y, typename... Ts>
    struct alternates<X, Y, X, Ts...>
    : alternates<Y, X, Ts...> {};

    struct C
    {


    We define the constructor as a template that first takes a parameter pack (the type will be deduced, no need to specify when calling) and it has a defaulted template parameter for SFINAE purposes. If the defaulted argument cannot be calculated based on the parameter pack, the constructor will not exist. I added the extra conditions about the number of pairs which I assumed from the example.



        template<typename... Ts,
    typename = typename std::enable_if<
    sizeof...(Ts) % 2 == 1 &&
    sizeof...(Ts) >= 3 && // did you imply this?
    alternates<A, B, Ts...>::value
    >::type>
    C(Ts&&...);
    };


    The way SFINAE works is that std::enable_if only defines the std::enable_if<condition, T>::type (the ::type part) if condition is true. That can be any arbitrary boolean expression computable at compile time. If it is false, saying ::type at the end will be a substitution failure and the overload where you tried to use it (e.g., C{A(), A(), A()}) will simply not be defined.



    You can test that the examples below work as expected. The ones commented out are not expected to work.



    int main() {
    C c1 { A(), B(), A() };
    C c2 { A(), B(), A(), B(), A(), B(), A() };
    // C c3 {}; // I assumed you need at least 2
    // C c4 { A(), B(), A(), A() }; // A, A doesn't alternate
    // C c5 { B(), A(), B() }; // B, A, B not allowed
    // C c6 { A(), B(), A(), B() }; // A, B, A, B doesn't pair
    }


    Try the code here.







    share|improve this answer














    share|improve this answer



    share|improve this answer








    edited yesterday

























    answered yesterday









    palotasb

    2,27211419




    2,27211419








    • 1




      I like this answer. One thing you can improve is that you don't need std::conditional: template <typename X, typename Y, typename... Ts> struct alternates<X, Y, X, Ts...> : alternates<Y, X, Ts...> {};
      – AndyG
      yesterday










    • I can't wait for compile-time reflection so that we can write type checks like this without using all these unreadable partial specialization-based techniques.
      – Brian
      yesterday










    • @AndyG thanks, edited.
      – palotasb
      yesterday










    • @Brian Might that also result in nicer error messages if I make the effort? I especially didn't like the error message of C c = {}: could not convert '<brace-enclosed initializer list>()' from '<brace-enclosed initializer list>' to 'C'
      – palotasb
      yesterday










    • @palotasb: You could write another ctor that has a !enable_if and then fails a static_assert on the interior
      – AndyG
      yesterday














    • 1




      I like this answer. One thing you can improve is that you don't need std::conditional: template <typename X, typename Y, typename... Ts> struct alternates<X, Y, X, Ts...> : alternates<Y, X, Ts...> {};
      – AndyG
      yesterday










    • I can't wait for compile-time reflection so that we can write type checks like this without using all these unreadable partial specialization-based techniques.
      – Brian
      yesterday










    • @AndyG thanks, edited.
      – palotasb
      yesterday










    • @Brian Might that also result in nicer error messages if I make the effort? I especially didn't like the error message of C c = {}: could not convert '<brace-enclosed initializer list>()' from '<brace-enclosed initializer list>' to 'C'
      – palotasb
      yesterday










    • @palotasb: You could write another ctor that has a !enable_if and then fails a static_assert on the interior
      – AndyG
      yesterday








    1




    1




    I like this answer. One thing you can improve is that you don't need std::conditional: template <typename X, typename Y, typename... Ts> struct alternates<X, Y, X, Ts...> : alternates<Y, X, Ts...> {};
    – AndyG
    yesterday




    I like this answer. One thing you can improve is that you don't need std::conditional: template <typename X, typename Y, typename... Ts> struct alternates<X, Y, X, Ts...> : alternates<Y, X, Ts...> {};
    – AndyG
    yesterday












    I can't wait for compile-time reflection so that we can write type checks like this without using all these unreadable partial specialization-based techniques.
    – Brian
    yesterday




    I can't wait for compile-time reflection so that we can write type checks like this without using all these unreadable partial specialization-based techniques.
    – Brian
    yesterday












    @AndyG thanks, edited.
    – palotasb
    yesterday




    @AndyG thanks, edited.
    – palotasb
    yesterday












    @Brian Might that also result in nicer error messages if I make the effort? I especially didn't like the error message of C c = {}: could not convert '<brace-enclosed initializer list>()' from '<brace-enclosed initializer list>' to 'C'
    – palotasb
    yesterday




    @Brian Might that also result in nicer error messages if I make the effort? I especially didn't like the error message of C c = {}: could not convert '<brace-enclosed initializer list>()' from '<brace-enclosed initializer list>' to 'C'
    – palotasb
    yesterday












    @palotasb: You could write another ctor that has a !enable_if and then fails a static_assert on the interior
    – AndyG
    yesterday




    @palotasb: You could write another ctor that has a !enable_if and then fails a static_assert on the interior
    – AndyG
    yesterday












    up vote
    5
    down vote













    I suppose you can use a template delegating constructor



    Something as follows



    #include <utility>

    struct A {};
    struct B {};

    struct C
    {
    C (A &&)
    { }

    template <typename ... Ts>
    C (A &&, B &&, Ts && ... ts) : C(std::forward<Ts>(ts)...)
    { }
    };

    int main()
    {
    C(A{});
    C(A{}, B{}, A{});
    C(A{}, B{}, A{}, B{}, A{});
    C(A{}, B{}, A{}, B{}, A{}, B{}, A{});
    }


    If you require at least three element (so no C(A{}) but at least C(A{}, B{}, A{})) the not-template constructor become



       C (A &&, B &&, A&&)
    { }





    share|improve this answer





















    • Well, the hard error can be irritating.
      – Deduplicator
      yesterday















    up vote
    5
    down vote













    I suppose you can use a template delegating constructor



    Something as follows



    #include <utility>

    struct A {};
    struct B {};

    struct C
    {
    C (A &&)
    { }

    template <typename ... Ts>
    C (A &&, B &&, Ts && ... ts) : C(std::forward<Ts>(ts)...)
    { }
    };

    int main()
    {
    C(A{});
    C(A{}, B{}, A{});
    C(A{}, B{}, A{}, B{}, A{});
    C(A{}, B{}, A{}, B{}, A{}, B{}, A{});
    }


    If you require at least three element (so no C(A{}) but at least C(A{}, B{}, A{})) the not-template constructor become



       C (A &&, B &&, A&&)
    { }





    share|improve this answer





















    • Well, the hard error can be irritating.
      – Deduplicator
      yesterday













    up vote
    5
    down vote










    up vote
    5
    down vote









    I suppose you can use a template delegating constructor



    Something as follows



    #include <utility>

    struct A {};
    struct B {};

    struct C
    {
    C (A &&)
    { }

    template <typename ... Ts>
    C (A &&, B &&, Ts && ... ts) : C(std::forward<Ts>(ts)...)
    { }
    };

    int main()
    {
    C(A{});
    C(A{}, B{}, A{});
    C(A{}, B{}, A{}, B{}, A{});
    C(A{}, B{}, A{}, B{}, A{}, B{}, A{});
    }


    If you require at least three element (so no C(A{}) but at least C(A{}, B{}, A{})) the not-template constructor become



       C (A &&, B &&, A&&)
    { }





    share|improve this answer












    I suppose you can use a template delegating constructor



    Something as follows



    #include <utility>

    struct A {};
    struct B {};

    struct C
    {
    C (A &&)
    { }

    template <typename ... Ts>
    C (A &&, B &&, Ts && ... ts) : C(std::forward<Ts>(ts)...)
    { }
    };

    int main()
    {
    C(A{});
    C(A{}, B{}, A{});
    C(A{}, B{}, A{}, B{}, A{});
    C(A{}, B{}, A{}, B{}, A{}, B{}, A{});
    }


    If you require at least three element (so no C(A{}) but at least C(A{}, B{}, A{})) the not-template constructor become



       C (A &&, B &&, A&&)
    { }






    share|improve this answer












    share|improve this answer



    share|improve this answer










    answered yesterday









    max66

    33.8k63762




    33.8k63762












    • Well, the hard error can be irritating.
      – Deduplicator
      yesterday


















    • Well, the hard error can be irritating.
      – Deduplicator
      yesterday
















    Well, the hard error can be irritating.
    – Deduplicator
    yesterday




    Well, the hard error can be irritating.
    – Deduplicator
    yesterday










    up vote
    0
    down vote













    Maybe something like this would help...



    #include <iostream>
    #include <vector>
    #include <utility>

    // Simple classes A & B to represent your alternating pattern classes.
    class A{ public: int a; };
    class B{ public: int b; };

    // helper class template to act as a single parameter kind of like std::pair...
    template<typename T, typename U>
    class Pack{
    private:
    T t_;
    U u_;

    public:
    Pack( T&& t, U&& u ) :
    t_( std::move( t ) ),
    u_( std::move( u ) )
    {}

    T getT() const { return t_; }
    U getU() const { return u_; }
    };

    // your class with varying amount of parameters for its ctors
    template<class T, class U>
    class C{
    private:
    std::vector<Pack<T,U>> packs_;

    public:
    template<typename... Packs>
    C( Packs&&... packs ) : packs_{ std::move( packs )... } { }

    std::vector<Pack<T,U>> getPacks() const {
    return packs_;
    }
    };

    // A few overloaded ostream operator<<()s for easy printing...
    std::ostream& operator<<( std::ostream& os, const A& a ) {
    os << a.a;
    return os;
    }

    std::ostream& operator<<( std::ostream& os, const B& b ) {
    os << b.b;
    return os;
    }

    template<typename T, typename U>
    std::ostream& operator<<( std::ostream& os, const Pack<T,U>& pack ) {
    os << pack.getT() << " " << pack.getU() << 'n';
    return os;
    }

    // Main program to demonstrate its use
    int main() {
    Pack<int,double> p1( 1, 2.3 ), p2( 4, 9.2 ), p3( 5, 3.5 );
    C<int, double> c( p1, p2, p3 );
    for (auto& p : c.getPacks() )
    std::cout << p;

    std::cout << 'n';

    Pack<float, char> p4( 3.14f, 'a' ), p5( 6.95f, 'b' ),
    p6( 2.81f, 'c' ), p7( 8.22f, 'd' );
    C<float, char> c2( p4, p5, p6, p7 );
    for ( auto& p : c2.getPacks() )
    std::cout << p;

    return 0;
    }


    Working Code



    -Output-



    1 2.3
    4 9.2
    5 3.5

    3.14 a
    6.95 b
    2.81 c
    8.22 d




    -Note- I did not incorporate for any odd number of parameters. For a more detailed solution with odd cases you can refer to the other answers with SFINAE or Delegating Constructor.






    share|improve this answer



























      up vote
      0
      down vote













      Maybe something like this would help...



      #include <iostream>
      #include <vector>
      #include <utility>

      // Simple classes A & B to represent your alternating pattern classes.
      class A{ public: int a; };
      class B{ public: int b; };

      // helper class template to act as a single parameter kind of like std::pair...
      template<typename T, typename U>
      class Pack{
      private:
      T t_;
      U u_;

      public:
      Pack( T&& t, U&& u ) :
      t_( std::move( t ) ),
      u_( std::move( u ) )
      {}

      T getT() const { return t_; }
      U getU() const { return u_; }
      };

      // your class with varying amount of parameters for its ctors
      template<class T, class U>
      class C{
      private:
      std::vector<Pack<T,U>> packs_;

      public:
      template<typename... Packs>
      C( Packs&&... packs ) : packs_{ std::move( packs )... } { }

      std::vector<Pack<T,U>> getPacks() const {
      return packs_;
      }
      };

      // A few overloaded ostream operator<<()s for easy printing...
      std::ostream& operator<<( std::ostream& os, const A& a ) {
      os << a.a;
      return os;
      }

      std::ostream& operator<<( std::ostream& os, const B& b ) {
      os << b.b;
      return os;
      }

      template<typename T, typename U>
      std::ostream& operator<<( std::ostream& os, const Pack<T,U>& pack ) {
      os << pack.getT() << " " << pack.getU() << 'n';
      return os;
      }

      // Main program to demonstrate its use
      int main() {
      Pack<int,double> p1( 1, 2.3 ), p2( 4, 9.2 ), p3( 5, 3.5 );
      C<int, double> c( p1, p2, p3 );
      for (auto& p : c.getPacks() )
      std::cout << p;

      std::cout << 'n';

      Pack<float, char> p4( 3.14f, 'a' ), p5( 6.95f, 'b' ),
      p6( 2.81f, 'c' ), p7( 8.22f, 'd' );
      C<float, char> c2( p4, p5, p6, p7 );
      for ( auto& p : c2.getPacks() )
      std::cout << p;

      return 0;
      }


      Working Code



      -Output-



      1 2.3
      4 9.2
      5 3.5

      3.14 a
      6.95 b
      2.81 c
      8.22 d




      -Note- I did not incorporate for any odd number of parameters. For a more detailed solution with odd cases you can refer to the other answers with SFINAE or Delegating Constructor.






      share|improve this answer

























        up vote
        0
        down vote










        up vote
        0
        down vote









        Maybe something like this would help...



        #include <iostream>
        #include <vector>
        #include <utility>

        // Simple classes A & B to represent your alternating pattern classes.
        class A{ public: int a; };
        class B{ public: int b; };

        // helper class template to act as a single parameter kind of like std::pair...
        template<typename T, typename U>
        class Pack{
        private:
        T t_;
        U u_;

        public:
        Pack( T&& t, U&& u ) :
        t_( std::move( t ) ),
        u_( std::move( u ) )
        {}

        T getT() const { return t_; }
        U getU() const { return u_; }
        };

        // your class with varying amount of parameters for its ctors
        template<class T, class U>
        class C{
        private:
        std::vector<Pack<T,U>> packs_;

        public:
        template<typename... Packs>
        C( Packs&&... packs ) : packs_{ std::move( packs )... } { }

        std::vector<Pack<T,U>> getPacks() const {
        return packs_;
        }
        };

        // A few overloaded ostream operator<<()s for easy printing...
        std::ostream& operator<<( std::ostream& os, const A& a ) {
        os << a.a;
        return os;
        }

        std::ostream& operator<<( std::ostream& os, const B& b ) {
        os << b.b;
        return os;
        }

        template<typename T, typename U>
        std::ostream& operator<<( std::ostream& os, const Pack<T,U>& pack ) {
        os << pack.getT() << " " << pack.getU() << 'n';
        return os;
        }

        // Main program to demonstrate its use
        int main() {
        Pack<int,double> p1( 1, 2.3 ), p2( 4, 9.2 ), p3( 5, 3.5 );
        C<int, double> c( p1, p2, p3 );
        for (auto& p : c.getPacks() )
        std::cout << p;

        std::cout << 'n';

        Pack<float, char> p4( 3.14f, 'a' ), p5( 6.95f, 'b' ),
        p6( 2.81f, 'c' ), p7( 8.22f, 'd' );
        C<float, char> c2( p4, p5, p6, p7 );
        for ( auto& p : c2.getPacks() )
        std::cout << p;

        return 0;
        }


        Working Code



        -Output-



        1 2.3
        4 9.2
        5 3.5

        3.14 a
        6.95 b
        2.81 c
        8.22 d




        -Note- I did not incorporate for any odd number of parameters. For a more detailed solution with odd cases you can refer to the other answers with SFINAE or Delegating Constructor.






        share|improve this answer














        Maybe something like this would help...



        #include <iostream>
        #include <vector>
        #include <utility>

        // Simple classes A & B to represent your alternating pattern classes.
        class A{ public: int a; };
        class B{ public: int b; };

        // helper class template to act as a single parameter kind of like std::pair...
        template<typename T, typename U>
        class Pack{
        private:
        T t_;
        U u_;

        public:
        Pack( T&& t, U&& u ) :
        t_( std::move( t ) ),
        u_( std::move( u ) )
        {}

        T getT() const { return t_; }
        U getU() const { return u_; }
        };

        // your class with varying amount of parameters for its ctors
        template<class T, class U>
        class C{
        private:
        std::vector<Pack<T,U>> packs_;

        public:
        template<typename... Packs>
        C( Packs&&... packs ) : packs_{ std::move( packs )... } { }

        std::vector<Pack<T,U>> getPacks() const {
        return packs_;
        }
        };

        // A few overloaded ostream operator<<()s for easy printing...
        std::ostream& operator<<( std::ostream& os, const A& a ) {
        os << a.a;
        return os;
        }

        std::ostream& operator<<( std::ostream& os, const B& b ) {
        os << b.b;
        return os;
        }

        template<typename T, typename U>
        std::ostream& operator<<( std::ostream& os, const Pack<T,U>& pack ) {
        os << pack.getT() << " " << pack.getU() << 'n';
        return os;
        }

        // Main program to demonstrate its use
        int main() {
        Pack<int,double> p1( 1, 2.3 ), p2( 4, 9.2 ), p3( 5, 3.5 );
        C<int, double> c( p1, p2, p3 );
        for (auto& p : c.getPacks() )
        std::cout << p;

        std::cout << 'n';

        Pack<float, char> p4( 3.14f, 'a' ), p5( 6.95f, 'b' ),
        p6( 2.81f, 'c' ), p7( 8.22f, 'd' );
        C<float, char> c2( p4, p5, p6, p7 );
        for ( auto& p : c2.getPacks() )
        std::cout << p;

        return 0;
        }


        Working Code



        -Output-



        1 2.3
        4 9.2
        5 3.5

        3.14 a
        6.95 b
        2.81 c
        8.22 d




        -Note- I did not incorporate for any odd number of parameters. For a more detailed solution with odd cases you can refer to the other answers with SFINAE or Delegating Constructor.







        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited 21 hours ago









        palotasb

        2,27211419




        2,27211419










        answered yesterday









        Francis Cugler

        4,22811227




        4,22811227






















            PeWe is a new contributor. Be nice, and check out our Code of Conduct.










            draft saved

            draft discarded


















            PeWe is a new contributor. Be nice, and check out our Code of Conduct.













            PeWe is a new contributor. Be nice, and check out our Code of Conduct.












            PeWe is a new contributor. Be nice, and check out our Code of Conduct.
















            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.





            Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


            Please pay close attention to the following guidance:


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53729854%2fparameter-pack-with-alternating-types%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            Plaza Victoria

            In PowerPoint, is there a keyboard shortcut for bulleted / numbered list?

            How to put 3 figures in Latex with 2 figures side by side and 1 below these side by side images but in...