Problem with calling a variadic function template when passing brace initialiser list arguments
up vote
14
down vote
favorite
Consider this function template:
template <class... T>
void foo (std::tuple<T, char, double> ... x);
This invocation works:
using K = std::tuple<int, char, double>;
foo ( K{1,'2',3.0}, K{4,'5',6.0}, K{7,'8',9.0} );
This one doesn't:
foo ( {1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0} );
(gcc and clang both complain about too many arguments for foo
)
Why is the second call a problem? Can I rewrite the declaration of foo
so that the second call is also accepted?
Thee template parameter T is only used to implement variadicity. The actual type is known and fixed, only the number of arguments varies. In real life the types are different from int, char, double
, this is just an example.
I cannot use C++17 for this. A C++11-compatible solution is much preferred.
c++ c++11 variadic-templates brace-initialization
|
show 8 more comments
up vote
14
down vote
favorite
Consider this function template:
template <class... T>
void foo (std::tuple<T, char, double> ... x);
This invocation works:
using K = std::tuple<int, char, double>;
foo ( K{1,'2',3.0}, K{4,'5',6.0}, K{7,'8',9.0} );
This one doesn't:
foo ( {1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0} );
(gcc and clang both complain about too many arguments for foo
)
Why is the second call a problem? Can I rewrite the declaration of foo
so that the second call is also accepted?
Thee template parameter T is only used to implement variadicity. The actual type is known and fixed, only the number of arguments varies. In real life the types are different from int, char, double
, this is just an example.
I cannot use C++17 for this. A C++11-compatible solution is much preferred.
c++ c++11 variadic-templates brace-initialization
2
this, this or this
– Piotr Skotnicki
Dec 9 at 12:45
1
@PiotrSkotnicki That looks promising for the OP's requirement(C++11). Then why you don't paste them as an answer? (Just curious)
– JeJo
Dec 9 at 12:52
1
With different types in it,{1, '2', 3.0}
can't be deduced asstd::initializer_list
or C-style array; and can't be deduced asstd::tuple<T, char, double>
because{1,
2, 3.0}
itself isn't astd::tuple
. I suppose you have to useK
, or explicit the type callingfoo()
(sofoo<int>( {1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0} );
) or avoid the braces, at least for the first triplet (sofoo(1,'2',3.0, {4,'5',6.0}, {7,'8',9.0})
) to permitT
deduction.
– max66
Dec 9 at 13:13
1
@n.m. then what's wrong with providing a sufficient number of overloads like here ?
– Piotr Skotnicki
Dec 9 at 13:20
1
My third hypothesis before require and additional couple of braces:foo(1,'2',3.0, {{4,'5',6.0}, {7,'8',9.0}})
. So the first1
is deduced asint
and the following triplets as astd::tuple<int, char, double>const [2]
– max66
Dec 9 at 13:20
|
show 8 more comments
up vote
14
down vote
favorite
up vote
14
down vote
favorite
Consider this function template:
template <class... T>
void foo (std::tuple<T, char, double> ... x);
This invocation works:
using K = std::tuple<int, char, double>;
foo ( K{1,'2',3.0}, K{4,'5',6.0}, K{7,'8',9.0} );
This one doesn't:
foo ( {1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0} );
(gcc and clang both complain about too many arguments for foo
)
Why is the second call a problem? Can I rewrite the declaration of foo
so that the second call is also accepted?
Thee template parameter T is only used to implement variadicity. The actual type is known and fixed, only the number of arguments varies. In real life the types are different from int, char, double
, this is just an example.
I cannot use C++17 for this. A C++11-compatible solution is much preferred.
c++ c++11 variadic-templates brace-initialization
Consider this function template:
template <class... T>
void foo (std::tuple<T, char, double> ... x);
This invocation works:
using K = std::tuple<int, char, double>;
foo ( K{1,'2',3.0}, K{4,'5',6.0}, K{7,'8',9.0} );
This one doesn't:
foo ( {1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0} );
(gcc and clang both complain about too many arguments for foo
)
Why is the second call a problem? Can I rewrite the declaration of foo
so that the second call is also accepted?
Thee template parameter T is only used to implement variadicity. The actual type is known and fixed, only the number of arguments varies. In real life the types are different from int, char, double
, this is just an example.
I cannot use C++17 for this. A C++11-compatible solution is much preferred.
c++ c++11 variadic-templates brace-initialization
c++ c++11 variadic-templates brace-initialization
edited Dec 9 at 13:09
asked Dec 9 at 11:29
n.m.
70.9k882166
70.9k882166
2
this, this or this
– Piotr Skotnicki
Dec 9 at 12:45
1
@PiotrSkotnicki That looks promising for the OP's requirement(C++11). Then why you don't paste them as an answer? (Just curious)
– JeJo
Dec 9 at 12:52
1
With different types in it,{1, '2', 3.0}
can't be deduced asstd::initializer_list
or C-style array; and can't be deduced asstd::tuple<T, char, double>
because{1,
2, 3.0}
itself isn't astd::tuple
. I suppose you have to useK
, or explicit the type callingfoo()
(sofoo<int>( {1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0} );
) or avoid the braces, at least for the first triplet (sofoo(1,'2',3.0, {4,'5',6.0}, {7,'8',9.0})
) to permitT
deduction.
– max66
Dec 9 at 13:13
1
@n.m. then what's wrong with providing a sufficient number of overloads like here ?
– Piotr Skotnicki
Dec 9 at 13:20
1
My third hypothesis before require and additional couple of braces:foo(1,'2',3.0, {{4,'5',6.0}, {7,'8',9.0}})
. So the first1
is deduced asint
and the following triplets as astd::tuple<int, char, double>const [2]
– max66
Dec 9 at 13:20
|
show 8 more comments
2
this, this or this
– Piotr Skotnicki
Dec 9 at 12:45
1
@PiotrSkotnicki That looks promising for the OP's requirement(C++11). Then why you don't paste them as an answer? (Just curious)
– JeJo
Dec 9 at 12:52
1
With different types in it,{1, '2', 3.0}
can't be deduced asstd::initializer_list
or C-style array; and can't be deduced asstd::tuple<T, char, double>
because{1,
2, 3.0}
itself isn't astd::tuple
. I suppose you have to useK
, or explicit the type callingfoo()
(sofoo<int>( {1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0} );
) or avoid the braces, at least for the first triplet (sofoo(1,'2',3.0, {4,'5',6.0}, {7,'8',9.0})
) to permitT
deduction.
– max66
Dec 9 at 13:13
1
@n.m. then what's wrong with providing a sufficient number of overloads like here ?
– Piotr Skotnicki
Dec 9 at 13:20
1
My third hypothesis before require and additional couple of braces:foo(1,'2',3.0, {{4,'5',6.0}, {7,'8',9.0}})
. So the first1
is deduced asint
and the following triplets as astd::tuple<int, char, double>const [2]
– max66
Dec 9 at 13:20
2
2
this, this or this
– Piotr Skotnicki
Dec 9 at 12:45
this, this or this
– Piotr Skotnicki
Dec 9 at 12:45
1
1
@PiotrSkotnicki That looks promising for the OP's requirement(C++11). Then why you don't paste them as an answer? (Just curious)
– JeJo
Dec 9 at 12:52
@PiotrSkotnicki That looks promising for the OP's requirement(C++11). Then why you don't paste them as an answer? (Just curious)
– JeJo
Dec 9 at 12:52
1
1
With different types in it,
{1, '2', 3.0}
can't be deduced as std::initializer_list
or C-style array; and can't be deduced as std::tuple<T, char, double>
because {1,
2, 3.0}
itself isn't a std::tuple
. I suppose you have to use K
, or explicit the type calling foo()
(so foo<int>( {1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0} );
) or avoid the braces, at least for the first triplet (so foo(1,'2',3.0, {4,'5',6.0}, {7,'8',9.0})
) to permit T
deduction.– max66
Dec 9 at 13:13
With different types in it,
{1, '2', 3.0}
can't be deduced as std::initializer_list
or C-style array; and can't be deduced as std::tuple<T, char, double>
because {1,
2, 3.0}
itself isn't a std::tuple
. I suppose you have to use K
, or explicit the type calling foo()
(so foo<int>( {1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0} );
) or avoid the braces, at least for the first triplet (so foo(1,'2',3.0, {4,'5',6.0}, {7,'8',9.0})
) to permit T
deduction.– max66
Dec 9 at 13:13
1
1
@n.m. then what's wrong with providing a sufficient number of overloads like here ?
– Piotr Skotnicki
Dec 9 at 13:20
@n.m. then what's wrong with providing a sufficient number of overloads like here ?
– Piotr Skotnicki
Dec 9 at 13:20
1
1
My third hypothesis before require and additional couple of braces:
foo(1,'2',3.0, {{4,'5',6.0}, {7,'8',9.0}})
. So the first 1
is deduced as int
and the following triplets as a std::tuple<int, char, double>const [2]
– max66
Dec 9 at 13:20
My third hypothesis before require and additional couple of braces:
foo(1,'2',3.0, {{4,'5',6.0}, {7,'8',9.0}})
. So the first 1
is deduced as int
and the following triplets as a std::tuple<int, char, double>const [2]
– max66
Dec 9 at 13:20
|
show 8 more comments
3 Answers
3
active
oldest
votes
up vote
8
down vote
accepted
Generate an overloaded set of constructors:
#include <tuple>
#include <cstddef>
template <typename T, std::size_t M>
using indexed = T;
template <typename T, std::size_t M, std::size_t... Is>
struct initializer : initializer<T, M, sizeof...(Is) + 1, Is...>
{
using initializer<T, M, sizeof...(Is) + 1, Is...>::initializer;
initializer(indexed<T, Is>... ts)
{
// ts is a pack of std::tuple<int, char, double>
}
};
template <typename T, std::size_t M, std::size_t... Is>
struct initializer<T, M, M, Is...> {};
using foo = initializer<std::tuple<int, char, double>, 20>;
// tuples limit+1 ~~~^
int main()
{
foo({1,'2',3.0});
foo({1,'2',3.0}, {4,'5',6.0});
foo({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0});
}
DEMO
Generate an overloaded set of function call operators:
#include <tuple>
#include <cstddef>
template <typename T, std::size_t M>
using indexed = T;
template <typename T, std::size_t M, std::size_t... Is>
struct initializer : initializer<T, M, sizeof...(Is) + 1, Is...>
{
using initializer<T, M, sizeof...(Is) + 1, Is...>::operator();
int operator()(indexed<T, Is>... ts) const
{
// ts is a pack of std::tuple<int, char, double>
return 1;
}
};
template <typename T, std::size_t M, std::size_t... Is>
struct initializer<T, M, M, Is...>
{
int operator()() const { return 0; }
};
static constexpr initializer<std::tuple<int, char, double>, 20> foo = {};
// tuples limit+1 ~~~^
int main()
{
foo({1,'2',3.0});
foo({1,'2',3.0}, {4,'5',6.0});
foo({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0});
}
DEMO 2
Create (or generate with preprocessor macros) a set of overloads that forward arguments to a single implementation:
#include <array>
#include <tuple>
using K = std::tuple<int, char, double>;
void foo(const std::array<K*, 5>& a)
{
// a is an array of at most 5 non-null std::tuple<int, char, double>*
}
void foo(K p0) { foo({&p0}); }
void foo(K p0, K p1) { foo({&p0, &p1}); }
void foo(K p0, K p1, K p2) { foo({&p0, &p1, &p2}); }
void foo(K p0, K p1, K p2, K p3) { foo({&p0, &p1, &p2, &p3}); }
void foo(K p0, K p1, K p2, K p3, K p4) { foo({&p0, &p1, &p2, &p3, &p4}); }
int main()
{
foo({1,'2',3.0});
foo({1,'2',3.0}, {4,'5',6.0});
foo({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0});
}
DEMO 3
Pass as an array and deduce its size (requires additional pair of parens):
#include <tuple>
#include <cstddef>
template <std::size_t N>
void foo(const std::tuple<int, char, double> (&a)[N])
{
// a is an array of exactly N std::tuple<int, char, double>
}
int main()
{
foo({{1,'2',3.0}, {4,'5',6.0}});
// ^~~~~~ extra parens ~~~~~^
}
DEMO 4
Use an std::initializer_list
as a constructor parameter (to skip extra parens):
#include <tuple>
#include <initializer_list>
struct foo
{
foo(std::initializer_list<std::tuple<int, char, double>> li)
{
// li is an initializer list of std::tuple<int, char, double>
}
};
int main()
{
foo{ {1,'2',3.0}, {4,'5',6.0} };
}
DEMO 5
intriguing the first one solution
– max66
Dec 9 at 14:25
add a comment |
up vote
4
down vote
{}
is not an expression hence don't have type, argument deduction is concerned about types, special care is taken when the argument used to perform argument deduction is an initializer list the template function parameter must have specifics forms, otherwise the parameter is a non-deduced context. A more simplistic example is this:
template <class T> struct A { T r; };
template <class T>
void foo (A<T> x);
using K = A<int>;
foo({1}); // fail
foo(K{1}); // compile
This is covered by [temp.deduc.call]/1
If removing references and cv-qualifiers from P gives
std::initializer_list<P'>
orP'[N]
for someP'
andN
and the argument is a non-empty initializer list ([dcl.init.list]), then deduction is performed instead for each element of the initializer list, takingP'
as a function template parameter type and the initializer element as its argument, and in theP'[N]
case, ifN
is a non-type template parameter,N
is deduced from the length of the initializer list. Otherwise, an initializer list argument causes the parameter to be considered a non-deduced context
and [temp.deduct.type]/5
The non-deduced contexts are:
(5.6) A function parameter for which the associated argument is an initializer list ([dcl.init.list]) but the parameter does not have a type for which deduction from an initializer list is specified ([temp.deduct.call]).
When you:
- explicitly provide template arguments, it works ... nothing to deduce
- specify the argument as
K{1}
, it works ... the argument is not longer an initializer list, is an expression with type.
add a comment |
up vote
0
down vote
I cannot use C++17 for this. A C++11-compatible solution is much preferred.
With C++11 is a little more complicated (no std::index_sequence
, no std::make_index_sequence
) but, if you want maintain the variadic use of tuples... that is... if you substantially want something as
foo (std::tuple<int, char, double> ... ts)
and if you accept to call a static method of a template struct, you can define a template struct that recursively inherit itself and, recursively, define a
func ();
func (K t0);
func (K t0, K t1);
func (K t0, K t1, K t2);
where K
is your
using K = std::tuple<int, char, double>;
The following is a full compiling C++11 example
#include <tuple>
#include <iostream>
using K = std::tuple<int, char, double>;
template <typename T, std::size_t>
struct getTypeStruct
{ using type = T; };
template <typename T, std::size_t N>
using getType = typename getTypeStruct<T, N>::type;
template <int ...>
struct iList;
template <std::size_t = 50u, std::size_t = 0u, typename = iList<>>
struct foo;
template <std::size_t Top, std::size_t N, int ... Is>
struct foo<Top, N, iList<Is...>> : public foo<Top, N+1u, iList<0, Is...>>
{
using foo<Top, N+1u, iList<0, Is...>>::func;
static void func (getType<K, Is> ... ts)
{ std::cout << sizeof...(ts) << std::endl; }
};
template <std::size_t Top, int ... Is>
struct foo<Top, Top, iList<Is...>>
{
// fake func, for recursion ground case
static void func ()
{ }
};
int main()
{
foo<>::func({1,'2',3.0}); // print 1
foo<>::func({1,'2',3.0}, {4,'5',6.0}); // print 2
foo<>::func({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0}); // print 3
}
If you can use C++14, you can use std::make_index_sequence
and std::index_sequence
and the code become a little better, IMHO
#include <tuple>
#include <iostream>
#include <type_traits>
using K = std::tuple<int, char, double>;
template <std::size_t ... Is>
constexpr auto getIndexSequence (std::index_sequence<Is...> is)
-> decltype(is);
template <std::size_t N>
using IndSeqFrom = decltype(getIndexSequence(std::make_index_sequence<N>{}));
template <typename T, std::size_t>
struct getTypeStruct
{ using type = T; };
template <typename T, std::size_t N>
using getType = typename getTypeStruct<T, N>::type;
template <std::size_t N = 50, typename = IndSeqFrom<N>>
struct foo;
template <std::size_t N, std::size_t ... Is>
struct foo<N, std::index_sequence<Is...>> : public foo<N-1u>
{
using foo<N-1u>::func;
static void func (getType<K, Is> ... ts)
{ std::cout << sizeof...(ts) << std::endl; }
};
template <>
struct foo<0, std::index_sequence<>>
{
static void func ()
{ std::cout << "0" << std::endl; }
};
int main()
{
foo<>::func({1,'2',3.0}); // print 1
foo<>::func({1,'2',3.0}, {4,'5',6.0}); // print 2
foo<>::func({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0}); // print 3
}
It's a pity you can't use C++17 because in you could use variadic unsing
and avoid at all recursive inheritance
#include <tuple>
#include <iostream>
#include <type_traits>
using K = std::tuple<int, char, double>;
template <std::size_t ... Is>
constexpr auto getIndexSequence (std::index_sequence<Is...> is)
-> decltype(is);
template <std::size_t N>
using IndSeqFrom = decltype(getIndexSequence(std::make_index_sequence<N>{}));
template <typename T, std::size_t>
struct getTypeStruct
{ using type = T; };
template <typename T, std::size_t N>
using getType = typename getTypeStruct<T, N>::type;
template <std::size_t N, typename = IndSeqFrom<N>>
struct bar;
template <std::size_t N, std::size_t ... Is>
struct bar<N, std::index_sequence<Is...>>
{
static void func (getType<K, Is> ... ts)
{ std::cout << sizeof...(ts) << std::endl; }
};
template <std::size_t N = 50, typename = IndSeqFrom<N>>
struct foo;
template <std::size_t N, std::size_t ... Is>
struct foo<N, std::index_sequence<Is...>> : public bar<Is>...
{ using bar<Is>::func...; };
int main()
{
foo<>::func({1,'2',3.0}); // print 1
foo<>::func({1,'2',3.0}, {4,'5',6.0}); // print 2
foo<>::func({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0}); // print 3
}
add a comment |
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53691855%2fproblem-with-calling-a-variadic-function-template-when-passing-brace-initialiser%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
8
down vote
accepted
Generate an overloaded set of constructors:
#include <tuple>
#include <cstddef>
template <typename T, std::size_t M>
using indexed = T;
template <typename T, std::size_t M, std::size_t... Is>
struct initializer : initializer<T, M, sizeof...(Is) + 1, Is...>
{
using initializer<T, M, sizeof...(Is) + 1, Is...>::initializer;
initializer(indexed<T, Is>... ts)
{
// ts is a pack of std::tuple<int, char, double>
}
};
template <typename T, std::size_t M, std::size_t... Is>
struct initializer<T, M, M, Is...> {};
using foo = initializer<std::tuple<int, char, double>, 20>;
// tuples limit+1 ~~~^
int main()
{
foo({1,'2',3.0});
foo({1,'2',3.0}, {4,'5',6.0});
foo({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0});
}
DEMO
Generate an overloaded set of function call operators:
#include <tuple>
#include <cstddef>
template <typename T, std::size_t M>
using indexed = T;
template <typename T, std::size_t M, std::size_t... Is>
struct initializer : initializer<T, M, sizeof...(Is) + 1, Is...>
{
using initializer<T, M, sizeof...(Is) + 1, Is...>::operator();
int operator()(indexed<T, Is>... ts) const
{
// ts is a pack of std::tuple<int, char, double>
return 1;
}
};
template <typename T, std::size_t M, std::size_t... Is>
struct initializer<T, M, M, Is...>
{
int operator()() const { return 0; }
};
static constexpr initializer<std::tuple<int, char, double>, 20> foo = {};
// tuples limit+1 ~~~^
int main()
{
foo({1,'2',3.0});
foo({1,'2',3.0}, {4,'5',6.0});
foo({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0});
}
DEMO 2
Create (or generate with preprocessor macros) a set of overloads that forward arguments to a single implementation:
#include <array>
#include <tuple>
using K = std::tuple<int, char, double>;
void foo(const std::array<K*, 5>& a)
{
// a is an array of at most 5 non-null std::tuple<int, char, double>*
}
void foo(K p0) { foo({&p0}); }
void foo(K p0, K p1) { foo({&p0, &p1}); }
void foo(K p0, K p1, K p2) { foo({&p0, &p1, &p2}); }
void foo(K p0, K p1, K p2, K p3) { foo({&p0, &p1, &p2, &p3}); }
void foo(K p0, K p1, K p2, K p3, K p4) { foo({&p0, &p1, &p2, &p3, &p4}); }
int main()
{
foo({1,'2',3.0});
foo({1,'2',3.0}, {4,'5',6.0});
foo({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0});
}
DEMO 3
Pass as an array and deduce its size (requires additional pair of parens):
#include <tuple>
#include <cstddef>
template <std::size_t N>
void foo(const std::tuple<int, char, double> (&a)[N])
{
// a is an array of exactly N std::tuple<int, char, double>
}
int main()
{
foo({{1,'2',3.0}, {4,'5',6.0}});
// ^~~~~~ extra parens ~~~~~^
}
DEMO 4
Use an std::initializer_list
as a constructor parameter (to skip extra parens):
#include <tuple>
#include <initializer_list>
struct foo
{
foo(std::initializer_list<std::tuple<int, char, double>> li)
{
// li is an initializer list of std::tuple<int, char, double>
}
};
int main()
{
foo{ {1,'2',3.0}, {4,'5',6.0} };
}
DEMO 5
intriguing the first one solution
– max66
Dec 9 at 14:25
add a comment |
up vote
8
down vote
accepted
Generate an overloaded set of constructors:
#include <tuple>
#include <cstddef>
template <typename T, std::size_t M>
using indexed = T;
template <typename T, std::size_t M, std::size_t... Is>
struct initializer : initializer<T, M, sizeof...(Is) + 1, Is...>
{
using initializer<T, M, sizeof...(Is) + 1, Is...>::initializer;
initializer(indexed<T, Is>... ts)
{
// ts is a pack of std::tuple<int, char, double>
}
};
template <typename T, std::size_t M, std::size_t... Is>
struct initializer<T, M, M, Is...> {};
using foo = initializer<std::tuple<int, char, double>, 20>;
// tuples limit+1 ~~~^
int main()
{
foo({1,'2',3.0});
foo({1,'2',3.0}, {4,'5',6.0});
foo({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0});
}
DEMO
Generate an overloaded set of function call operators:
#include <tuple>
#include <cstddef>
template <typename T, std::size_t M>
using indexed = T;
template <typename T, std::size_t M, std::size_t... Is>
struct initializer : initializer<T, M, sizeof...(Is) + 1, Is...>
{
using initializer<T, M, sizeof...(Is) + 1, Is...>::operator();
int operator()(indexed<T, Is>... ts) const
{
// ts is a pack of std::tuple<int, char, double>
return 1;
}
};
template <typename T, std::size_t M, std::size_t... Is>
struct initializer<T, M, M, Is...>
{
int operator()() const { return 0; }
};
static constexpr initializer<std::tuple<int, char, double>, 20> foo = {};
// tuples limit+1 ~~~^
int main()
{
foo({1,'2',3.0});
foo({1,'2',3.0}, {4,'5',6.0});
foo({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0});
}
DEMO 2
Create (or generate with preprocessor macros) a set of overloads that forward arguments to a single implementation:
#include <array>
#include <tuple>
using K = std::tuple<int, char, double>;
void foo(const std::array<K*, 5>& a)
{
// a is an array of at most 5 non-null std::tuple<int, char, double>*
}
void foo(K p0) { foo({&p0}); }
void foo(K p0, K p1) { foo({&p0, &p1}); }
void foo(K p0, K p1, K p2) { foo({&p0, &p1, &p2}); }
void foo(K p0, K p1, K p2, K p3) { foo({&p0, &p1, &p2, &p3}); }
void foo(K p0, K p1, K p2, K p3, K p4) { foo({&p0, &p1, &p2, &p3, &p4}); }
int main()
{
foo({1,'2',3.0});
foo({1,'2',3.0}, {4,'5',6.0});
foo({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0});
}
DEMO 3
Pass as an array and deduce its size (requires additional pair of parens):
#include <tuple>
#include <cstddef>
template <std::size_t N>
void foo(const std::tuple<int, char, double> (&a)[N])
{
// a is an array of exactly N std::tuple<int, char, double>
}
int main()
{
foo({{1,'2',3.0}, {4,'5',6.0}});
// ^~~~~~ extra parens ~~~~~^
}
DEMO 4
Use an std::initializer_list
as a constructor parameter (to skip extra parens):
#include <tuple>
#include <initializer_list>
struct foo
{
foo(std::initializer_list<std::tuple<int, char, double>> li)
{
// li is an initializer list of std::tuple<int, char, double>
}
};
int main()
{
foo{ {1,'2',3.0}, {4,'5',6.0} };
}
DEMO 5
intriguing the first one solution
– max66
Dec 9 at 14:25
add a comment |
up vote
8
down vote
accepted
up vote
8
down vote
accepted
Generate an overloaded set of constructors:
#include <tuple>
#include <cstddef>
template <typename T, std::size_t M>
using indexed = T;
template <typename T, std::size_t M, std::size_t... Is>
struct initializer : initializer<T, M, sizeof...(Is) + 1, Is...>
{
using initializer<T, M, sizeof...(Is) + 1, Is...>::initializer;
initializer(indexed<T, Is>... ts)
{
// ts is a pack of std::tuple<int, char, double>
}
};
template <typename T, std::size_t M, std::size_t... Is>
struct initializer<T, M, M, Is...> {};
using foo = initializer<std::tuple<int, char, double>, 20>;
// tuples limit+1 ~~~^
int main()
{
foo({1,'2',3.0});
foo({1,'2',3.0}, {4,'5',6.0});
foo({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0});
}
DEMO
Generate an overloaded set of function call operators:
#include <tuple>
#include <cstddef>
template <typename T, std::size_t M>
using indexed = T;
template <typename T, std::size_t M, std::size_t... Is>
struct initializer : initializer<T, M, sizeof...(Is) + 1, Is...>
{
using initializer<T, M, sizeof...(Is) + 1, Is...>::operator();
int operator()(indexed<T, Is>... ts) const
{
// ts is a pack of std::tuple<int, char, double>
return 1;
}
};
template <typename T, std::size_t M, std::size_t... Is>
struct initializer<T, M, M, Is...>
{
int operator()() const { return 0; }
};
static constexpr initializer<std::tuple<int, char, double>, 20> foo = {};
// tuples limit+1 ~~~^
int main()
{
foo({1,'2',3.0});
foo({1,'2',3.0}, {4,'5',6.0});
foo({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0});
}
DEMO 2
Create (or generate with preprocessor macros) a set of overloads that forward arguments to a single implementation:
#include <array>
#include <tuple>
using K = std::tuple<int, char, double>;
void foo(const std::array<K*, 5>& a)
{
// a is an array of at most 5 non-null std::tuple<int, char, double>*
}
void foo(K p0) { foo({&p0}); }
void foo(K p0, K p1) { foo({&p0, &p1}); }
void foo(K p0, K p1, K p2) { foo({&p0, &p1, &p2}); }
void foo(K p0, K p1, K p2, K p3) { foo({&p0, &p1, &p2, &p3}); }
void foo(K p0, K p1, K p2, K p3, K p4) { foo({&p0, &p1, &p2, &p3, &p4}); }
int main()
{
foo({1,'2',3.0});
foo({1,'2',3.0}, {4,'5',6.0});
foo({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0});
}
DEMO 3
Pass as an array and deduce its size (requires additional pair of parens):
#include <tuple>
#include <cstddef>
template <std::size_t N>
void foo(const std::tuple<int, char, double> (&a)[N])
{
// a is an array of exactly N std::tuple<int, char, double>
}
int main()
{
foo({{1,'2',3.0}, {4,'5',6.0}});
// ^~~~~~ extra parens ~~~~~^
}
DEMO 4
Use an std::initializer_list
as a constructor parameter (to skip extra parens):
#include <tuple>
#include <initializer_list>
struct foo
{
foo(std::initializer_list<std::tuple<int, char, double>> li)
{
// li is an initializer list of std::tuple<int, char, double>
}
};
int main()
{
foo{ {1,'2',3.0}, {4,'5',6.0} };
}
DEMO 5
Generate an overloaded set of constructors:
#include <tuple>
#include <cstddef>
template <typename T, std::size_t M>
using indexed = T;
template <typename T, std::size_t M, std::size_t... Is>
struct initializer : initializer<T, M, sizeof...(Is) + 1, Is...>
{
using initializer<T, M, sizeof...(Is) + 1, Is...>::initializer;
initializer(indexed<T, Is>... ts)
{
// ts is a pack of std::tuple<int, char, double>
}
};
template <typename T, std::size_t M, std::size_t... Is>
struct initializer<T, M, M, Is...> {};
using foo = initializer<std::tuple<int, char, double>, 20>;
// tuples limit+1 ~~~^
int main()
{
foo({1,'2',3.0});
foo({1,'2',3.0}, {4,'5',6.0});
foo({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0});
}
DEMO
Generate an overloaded set of function call operators:
#include <tuple>
#include <cstddef>
template <typename T, std::size_t M>
using indexed = T;
template <typename T, std::size_t M, std::size_t... Is>
struct initializer : initializer<T, M, sizeof...(Is) + 1, Is...>
{
using initializer<T, M, sizeof...(Is) + 1, Is...>::operator();
int operator()(indexed<T, Is>... ts) const
{
// ts is a pack of std::tuple<int, char, double>
return 1;
}
};
template <typename T, std::size_t M, std::size_t... Is>
struct initializer<T, M, M, Is...>
{
int operator()() const { return 0; }
};
static constexpr initializer<std::tuple<int, char, double>, 20> foo = {};
// tuples limit+1 ~~~^
int main()
{
foo({1,'2',3.0});
foo({1,'2',3.0}, {4,'5',6.0});
foo({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0});
}
DEMO 2
Create (or generate with preprocessor macros) a set of overloads that forward arguments to a single implementation:
#include <array>
#include <tuple>
using K = std::tuple<int, char, double>;
void foo(const std::array<K*, 5>& a)
{
// a is an array of at most 5 non-null std::tuple<int, char, double>*
}
void foo(K p0) { foo({&p0}); }
void foo(K p0, K p1) { foo({&p0, &p1}); }
void foo(K p0, K p1, K p2) { foo({&p0, &p1, &p2}); }
void foo(K p0, K p1, K p2, K p3) { foo({&p0, &p1, &p2, &p3}); }
void foo(K p0, K p1, K p2, K p3, K p4) { foo({&p0, &p1, &p2, &p3, &p4}); }
int main()
{
foo({1,'2',3.0});
foo({1,'2',3.0}, {4,'5',6.0});
foo({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0});
}
DEMO 3
Pass as an array and deduce its size (requires additional pair of parens):
#include <tuple>
#include <cstddef>
template <std::size_t N>
void foo(const std::tuple<int, char, double> (&a)[N])
{
// a is an array of exactly N std::tuple<int, char, double>
}
int main()
{
foo({{1,'2',3.0}, {4,'5',6.0}});
// ^~~~~~ extra parens ~~~~~^
}
DEMO 4
Use an std::initializer_list
as a constructor parameter (to skip extra parens):
#include <tuple>
#include <initializer_list>
struct foo
{
foo(std::initializer_list<std::tuple<int, char, double>> li)
{
// li is an initializer list of std::tuple<int, char, double>
}
};
int main()
{
foo{ {1,'2',3.0}, {4,'5',6.0} };
}
DEMO 5
edited Dec 9 at 15:35
answered Dec 9 at 14:14
Piotr Skotnicki
34.4k470117
34.4k470117
intriguing the first one solution
– max66
Dec 9 at 14:25
add a comment |
intriguing the first one solution
– max66
Dec 9 at 14:25
intriguing the first one solution
– max66
Dec 9 at 14:25
intriguing the first one solution
– max66
Dec 9 at 14:25
add a comment |
up vote
4
down vote
{}
is not an expression hence don't have type, argument deduction is concerned about types, special care is taken when the argument used to perform argument deduction is an initializer list the template function parameter must have specifics forms, otherwise the parameter is a non-deduced context. A more simplistic example is this:
template <class T> struct A { T r; };
template <class T>
void foo (A<T> x);
using K = A<int>;
foo({1}); // fail
foo(K{1}); // compile
This is covered by [temp.deduc.call]/1
If removing references and cv-qualifiers from P gives
std::initializer_list<P'>
orP'[N]
for someP'
andN
and the argument is a non-empty initializer list ([dcl.init.list]), then deduction is performed instead for each element of the initializer list, takingP'
as a function template parameter type and the initializer element as its argument, and in theP'[N]
case, ifN
is a non-type template parameter,N
is deduced from the length of the initializer list. Otherwise, an initializer list argument causes the parameter to be considered a non-deduced context
and [temp.deduct.type]/5
The non-deduced contexts are:
(5.6) A function parameter for which the associated argument is an initializer list ([dcl.init.list]) but the parameter does not have a type for which deduction from an initializer list is specified ([temp.deduct.call]).
When you:
- explicitly provide template arguments, it works ... nothing to deduce
- specify the argument as
K{1}
, it works ... the argument is not longer an initializer list, is an expression with type.
add a comment |
up vote
4
down vote
{}
is not an expression hence don't have type, argument deduction is concerned about types, special care is taken when the argument used to perform argument deduction is an initializer list the template function parameter must have specifics forms, otherwise the parameter is a non-deduced context. A more simplistic example is this:
template <class T> struct A { T r; };
template <class T>
void foo (A<T> x);
using K = A<int>;
foo({1}); // fail
foo(K{1}); // compile
This is covered by [temp.deduc.call]/1
If removing references and cv-qualifiers from P gives
std::initializer_list<P'>
orP'[N]
for someP'
andN
and the argument is a non-empty initializer list ([dcl.init.list]), then deduction is performed instead for each element of the initializer list, takingP'
as a function template parameter type and the initializer element as its argument, and in theP'[N]
case, ifN
is a non-type template parameter,N
is deduced from the length of the initializer list. Otherwise, an initializer list argument causes the parameter to be considered a non-deduced context
and [temp.deduct.type]/5
The non-deduced contexts are:
(5.6) A function parameter for which the associated argument is an initializer list ([dcl.init.list]) but the parameter does not have a type for which deduction from an initializer list is specified ([temp.deduct.call]).
When you:
- explicitly provide template arguments, it works ... nothing to deduce
- specify the argument as
K{1}
, it works ... the argument is not longer an initializer list, is an expression with type.
add a comment |
up vote
4
down vote
up vote
4
down vote
{}
is not an expression hence don't have type, argument deduction is concerned about types, special care is taken when the argument used to perform argument deduction is an initializer list the template function parameter must have specifics forms, otherwise the parameter is a non-deduced context. A more simplistic example is this:
template <class T> struct A { T r; };
template <class T>
void foo (A<T> x);
using K = A<int>;
foo({1}); // fail
foo(K{1}); // compile
This is covered by [temp.deduc.call]/1
If removing references and cv-qualifiers from P gives
std::initializer_list<P'>
orP'[N]
for someP'
andN
and the argument is a non-empty initializer list ([dcl.init.list]), then deduction is performed instead for each element of the initializer list, takingP'
as a function template parameter type and the initializer element as its argument, and in theP'[N]
case, ifN
is a non-type template parameter,N
is deduced from the length of the initializer list. Otherwise, an initializer list argument causes the parameter to be considered a non-deduced context
and [temp.deduct.type]/5
The non-deduced contexts are:
(5.6) A function parameter for which the associated argument is an initializer list ([dcl.init.list]) but the parameter does not have a type for which deduction from an initializer list is specified ([temp.deduct.call]).
When you:
- explicitly provide template arguments, it works ... nothing to deduce
- specify the argument as
K{1}
, it works ... the argument is not longer an initializer list, is an expression with type.
{}
is not an expression hence don't have type, argument deduction is concerned about types, special care is taken when the argument used to perform argument deduction is an initializer list the template function parameter must have specifics forms, otherwise the parameter is a non-deduced context. A more simplistic example is this:
template <class T> struct A { T r; };
template <class T>
void foo (A<T> x);
using K = A<int>;
foo({1}); // fail
foo(K{1}); // compile
This is covered by [temp.deduc.call]/1
If removing references and cv-qualifiers from P gives
std::initializer_list<P'>
orP'[N]
for someP'
andN
and the argument is a non-empty initializer list ([dcl.init.list]), then deduction is performed instead for each element of the initializer list, takingP'
as a function template parameter type and the initializer element as its argument, and in theP'[N]
case, ifN
is a non-type template parameter,N
is deduced from the length of the initializer list. Otherwise, an initializer list argument causes the parameter to be considered a non-deduced context
and [temp.deduct.type]/5
The non-deduced contexts are:
(5.6) A function parameter for which the associated argument is an initializer list ([dcl.init.list]) but the parameter does not have a type for which deduction from an initializer list is specified ([temp.deduct.call]).
When you:
- explicitly provide template arguments, it works ... nothing to deduce
- specify the argument as
K{1}
, it works ... the argument is not longer an initializer list, is an expression with type.
edited Dec 9 at 13:47
answered Dec 9 at 13:40
Jans
7,14422233
7,14422233
add a comment |
add a comment |
up vote
0
down vote
I cannot use C++17 for this. A C++11-compatible solution is much preferred.
With C++11 is a little more complicated (no std::index_sequence
, no std::make_index_sequence
) but, if you want maintain the variadic use of tuples... that is... if you substantially want something as
foo (std::tuple<int, char, double> ... ts)
and if you accept to call a static method of a template struct, you can define a template struct that recursively inherit itself and, recursively, define a
func ();
func (K t0);
func (K t0, K t1);
func (K t0, K t1, K t2);
where K
is your
using K = std::tuple<int, char, double>;
The following is a full compiling C++11 example
#include <tuple>
#include <iostream>
using K = std::tuple<int, char, double>;
template <typename T, std::size_t>
struct getTypeStruct
{ using type = T; };
template <typename T, std::size_t N>
using getType = typename getTypeStruct<T, N>::type;
template <int ...>
struct iList;
template <std::size_t = 50u, std::size_t = 0u, typename = iList<>>
struct foo;
template <std::size_t Top, std::size_t N, int ... Is>
struct foo<Top, N, iList<Is...>> : public foo<Top, N+1u, iList<0, Is...>>
{
using foo<Top, N+1u, iList<0, Is...>>::func;
static void func (getType<K, Is> ... ts)
{ std::cout << sizeof...(ts) << std::endl; }
};
template <std::size_t Top, int ... Is>
struct foo<Top, Top, iList<Is...>>
{
// fake func, for recursion ground case
static void func ()
{ }
};
int main()
{
foo<>::func({1,'2',3.0}); // print 1
foo<>::func({1,'2',3.0}, {4,'5',6.0}); // print 2
foo<>::func({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0}); // print 3
}
If you can use C++14, you can use std::make_index_sequence
and std::index_sequence
and the code become a little better, IMHO
#include <tuple>
#include <iostream>
#include <type_traits>
using K = std::tuple<int, char, double>;
template <std::size_t ... Is>
constexpr auto getIndexSequence (std::index_sequence<Is...> is)
-> decltype(is);
template <std::size_t N>
using IndSeqFrom = decltype(getIndexSequence(std::make_index_sequence<N>{}));
template <typename T, std::size_t>
struct getTypeStruct
{ using type = T; };
template <typename T, std::size_t N>
using getType = typename getTypeStruct<T, N>::type;
template <std::size_t N = 50, typename = IndSeqFrom<N>>
struct foo;
template <std::size_t N, std::size_t ... Is>
struct foo<N, std::index_sequence<Is...>> : public foo<N-1u>
{
using foo<N-1u>::func;
static void func (getType<K, Is> ... ts)
{ std::cout << sizeof...(ts) << std::endl; }
};
template <>
struct foo<0, std::index_sequence<>>
{
static void func ()
{ std::cout << "0" << std::endl; }
};
int main()
{
foo<>::func({1,'2',3.0}); // print 1
foo<>::func({1,'2',3.0}, {4,'5',6.0}); // print 2
foo<>::func({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0}); // print 3
}
It's a pity you can't use C++17 because in you could use variadic unsing
and avoid at all recursive inheritance
#include <tuple>
#include <iostream>
#include <type_traits>
using K = std::tuple<int, char, double>;
template <std::size_t ... Is>
constexpr auto getIndexSequence (std::index_sequence<Is...> is)
-> decltype(is);
template <std::size_t N>
using IndSeqFrom = decltype(getIndexSequence(std::make_index_sequence<N>{}));
template <typename T, std::size_t>
struct getTypeStruct
{ using type = T; };
template <typename T, std::size_t N>
using getType = typename getTypeStruct<T, N>::type;
template <std::size_t N, typename = IndSeqFrom<N>>
struct bar;
template <std::size_t N, std::size_t ... Is>
struct bar<N, std::index_sequence<Is...>>
{
static void func (getType<K, Is> ... ts)
{ std::cout << sizeof...(ts) << std::endl; }
};
template <std::size_t N = 50, typename = IndSeqFrom<N>>
struct foo;
template <std::size_t N, std::size_t ... Is>
struct foo<N, std::index_sequence<Is...>> : public bar<Is>...
{ using bar<Is>::func...; };
int main()
{
foo<>::func({1,'2',3.0}); // print 1
foo<>::func({1,'2',3.0}, {4,'5',6.0}); // print 2
foo<>::func({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0}); // print 3
}
add a comment |
up vote
0
down vote
I cannot use C++17 for this. A C++11-compatible solution is much preferred.
With C++11 is a little more complicated (no std::index_sequence
, no std::make_index_sequence
) but, if you want maintain the variadic use of tuples... that is... if you substantially want something as
foo (std::tuple<int, char, double> ... ts)
and if you accept to call a static method of a template struct, you can define a template struct that recursively inherit itself and, recursively, define a
func ();
func (K t0);
func (K t0, K t1);
func (K t0, K t1, K t2);
where K
is your
using K = std::tuple<int, char, double>;
The following is a full compiling C++11 example
#include <tuple>
#include <iostream>
using K = std::tuple<int, char, double>;
template <typename T, std::size_t>
struct getTypeStruct
{ using type = T; };
template <typename T, std::size_t N>
using getType = typename getTypeStruct<T, N>::type;
template <int ...>
struct iList;
template <std::size_t = 50u, std::size_t = 0u, typename = iList<>>
struct foo;
template <std::size_t Top, std::size_t N, int ... Is>
struct foo<Top, N, iList<Is...>> : public foo<Top, N+1u, iList<0, Is...>>
{
using foo<Top, N+1u, iList<0, Is...>>::func;
static void func (getType<K, Is> ... ts)
{ std::cout << sizeof...(ts) << std::endl; }
};
template <std::size_t Top, int ... Is>
struct foo<Top, Top, iList<Is...>>
{
// fake func, for recursion ground case
static void func ()
{ }
};
int main()
{
foo<>::func({1,'2',3.0}); // print 1
foo<>::func({1,'2',3.0}, {4,'5',6.0}); // print 2
foo<>::func({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0}); // print 3
}
If you can use C++14, you can use std::make_index_sequence
and std::index_sequence
and the code become a little better, IMHO
#include <tuple>
#include <iostream>
#include <type_traits>
using K = std::tuple<int, char, double>;
template <std::size_t ... Is>
constexpr auto getIndexSequence (std::index_sequence<Is...> is)
-> decltype(is);
template <std::size_t N>
using IndSeqFrom = decltype(getIndexSequence(std::make_index_sequence<N>{}));
template <typename T, std::size_t>
struct getTypeStruct
{ using type = T; };
template <typename T, std::size_t N>
using getType = typename getTypeStruct<T, N>::type;
template <std::size_t N = 50, typename = IndSeqFrom<N>>
struct foo;
template <std::size_t N, std::size_t ... Is>
struct foo<N, std::index_sequence<Is...>> : public foo<N-1u>
{
using foo<N-1u>::func;
static void func (getType<K, Is> ... ts)
{ std::cout << sizeof...(ts) << std::endl; }
};
template <>
struct foo<0, std::index_sequence<>>
{
static void func ()
{ std::cout << "0" << std::endl; }
};
int main()
{
foo<>::func({1,'2',3.0}); // print 1
foo<>::func({1,'2',3.0}, {4,'5',6.0}); // print 2
foo<>::func({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0}); // print 3
}
It's a pity you can't use C++17 because in you could use variadic unsing
and avoid at all recursive inheritance
#include <tuple>
#include <iostream>
#include <type_traits>
using K = std::tuple<int, char, double>;
template <std::size_t ... Is>
constexpr auto getIndexSequence (std::index_sequence<Is...> is)
-> decltype(is);
template <std::size_t N>
using IndSeqFrom = decltype(getIndexSequence(std::make_index_sequence<N>{}));
template <typename T, std::size_t>
struct getTypeStruct
{ using type = T; };
template <typename T, std::size_t N>
using getType = typename getTypeStruct<T, N>::type;
template <std::size_t N, typename = IndSeqFrom<N>>
struct bar;
template <std::size_t N, std::size_t ... Is>
struct bar<N, std::index_sequence<Is...>>
{
static void func (getType<K, Is> ... ts)
{ std::cout << sizeof...(ts) << std::endl; }
};
template <std::size_t N = 50, typename = IndSeqFrom<N>>
struct foo;
template <std::size_t N, std::size_t ... Is>
struct foo<N, std::index_sequence<Is...>> : public bar<Is>...
{ using bar<Is>::func...; };
int main()
{
foo<>::func({1,'2',3.0}); // print 1
foo<>::func({1,'2',3.0}, {4,'5',6.0}); // print 2
foo<>::func({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0}); // print 3
}
add a comment |
up vote
0
down vote
up vote
0
down vote
I cannot use C++17 for this. A C++11-compatible solution is much preferred.
With C++11 is a little more complicated (no std::index_sequence
, no std::make_index_sequence
) but, if you want maintain the variadic use of tuples... that is... if you substantially want something as
foo (std::tuple<int, char, double> ... ts)
and if you accept to call a static method of a template struct, you can define a template struct that recursively inherit itself and, recursively, define a
func ();
func (K t0);
func (K t0, K t1);
func (K t0, K t1, K t2);
where K
is your
using K = std::tuple<int, char, double>;
The following is a full compiling C++11 example
#include <tuple>
#include <iostream>
using K = std::tuple<int, char, double>;
template <typename T, std::size_t>
struct getTypeStruct
{ using type = T; };
template <typename T, std::size_t N>
using getType = typename getTypeStruct<T, N>::type;
template <int ...>
struct iList;
template <std::size_t = 50u, std::size_t = 0u, typename = iList<>>
struct foo;
template <std::size_t Top, std::size_t N, int ... Is>
struct foo<Top, N, iList<Is...>> : public foo<Top, N+1u, iList<0, Is...>>
{
using foo<Top, N+1u, iList<0, Is...>>::func;
static void func (getType<K, Is> ... ts)
{ std::cout << sizeof...(ts) << std::endl; }
};
template <std::size_t Top, int ... Is>
struct foo<Top, Top, iList<Is...>>
{
// fake func, for recursion ground case
static void func ()
{ }
};
int main()
{
foo<>::func({1,'2',3.0}); // print 1
foo<>::func({1,'2',3.0}, {4,'5',6.0}); // print 2
foo<>::func({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0}); // print 3
}
If you can use C++14, you can use std::make_index_sequence
and std::index_sequence
and the code become a little better, IMHO
#include <tuple>
#include <iostream>
#include <type_traits>
using K = std::tuple<int, char, double>;
template <std::size_t ... Is>
constexpr auto getIndexSequence (std::index_sequence<Is...> is)
-> decltype(is);
template <std::size_t N>
using IndSeqFrom = decltype(getIndexSequence(std::make_index_sequence<N>{}));
template <typename T, std::size_t>
struct getTypeStruct
{ using type = T; };
template <typename T, std::size_t N>
using getType = typename getTypeStruct<T, N>::type;
template <std::size_t N = 50, typename = IndSeqFrom<N>>
struct foo;
template <std::size_t N, std::size_t ... Is>
struct foo<N, std::index_sequence<Is...>> : public foo<N-1u>
{
using foo<N-1u>::func;
static void func (getType<K, Is> ... ts)
{ std::cout << sizeof...(ts) << std::endl; }
};
template <>
struct foo<0, std::index_sequence<>>
{
static void func ()
{ std::cout << "0" << std::endl; }
};
int main()
{
foo<>::func({1,'2',3.0}); // print 1
foo<>::func({1,'2',3.0}, {4,'5',6.0}); // print 2
foo<>::func({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0}); // print 3
}
It's a pity you can't use C++17 because in you could use variadic unsing
and avoid at all recursive inheritance
#include <tuple>
#include <iostream>
#include <type_traits>
using K = std::tuple<int, char, double>;
template <std::size_t ... Is>
constexpr auto getIndexSequence (std::index_sequence<Is...> is)
-> decltype(is);
template <std::size_t N>
using IndSeqFrom = decltype(getIndexSequence(std::make_index_sequence<N>{}));
template <typename T, std::size_t>
struct getTypeStruct
{ using type = T; };
template <typename T, std::size_t N>
using getType = typename getTypeStruct<T, N>::type;
template <std::size_t N, typename = IndSeqFrom<N>>
struct bar;
template <std::size_t N, std::size_t ... Is>
struct bar<N, std::index_sequence<Is...>>
{
static void func (getType<K, Is> ... ts)
{ std::cout << sizeof...(ts) << std::endl; }
};
template <std::size_t N = 50, typename = IndSeqFrom<N>>
struct foo;
template <std::size_t N, std::size_t ... Is>
struct foo<N, std::index_sequence<Is...>> : public bar<Is>...
{ using bar<Is>::func...; };
int main()
{
foo<>::func({1,'2',3.0}); // print 1
foo<>::func({1,'2',3.0}, {4,'5',6.0}); // print 2
foo<>::func({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0}); // print 3
}
I cannot use C++17 for this. A C++11-compatible solution is much preferred.
With C++11 is a little more complicated (no std::index_sequence
, no std::make_index_sequence
) but, if you want maintain the variadic use of tuples... that is... if you substantially want something as
foo (std::tuple<int, char, double> ... ts)
and if you accept to call a static method of a template struct, you can define a template struct that recursively inherit itself and, recursively, define a
func ();
func (K t0);
func (K t0, K t1);
func (K t0, K t1, K t2);
where K
is your
using K = std::tuple<int, char, double>;
The following is a full compiling C++11 example
#include <tuple>
#include <iostream>
using K = std::tuple<int, char, double>;
template <typename T, std::size_t>
struct getTypeStruct
{ using type = T; };
template <typename T, std::size_t N>
using getType = typename getTypeStruct<T, N>::type;
template <int ...>
struct iList;
template <std::size_t = 50u, std::size_t = 0u, typename = iList<>>
struct foo;
template <std::size_t Top, std::size_t N, int ... Is>
struct foo<Top, N, iList<Is...>> : public foo<Top, N+1u, iList<0, Is...>>
{
using foo<Top, N+1u, iList<0, Is...>>::func;
static void func (getType<K, Is> ... ts)
{ std::cout << sizeof...(ts) << std::endl; }
};
template <std::size_t Top, int ... Is>
struct foo<Top, Top, iList<Is...>>
{
// fake func, for recursion ground case
static void func ()
{ }
};
int main()
{
foo<>::func({1,'2',3.0}); // print 1
foo<>::func({1,'2',3.0}, {4,'5',6.0}); // print 2
foo<>::func({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0}); // print 3
}
If you can use C++14, you can use std::make_index_sequence
and std::index_sequence
and the code become a little better, IMHO
#include <tuple>
#include <iostream>
#include <type_traits>
using K = std::tuple<int, char, double>;
template <std::size_t ... Is>
constexpr auto getIndexSequence (std::index_sequence<Is...> is)
-> decltype(is);
template <std::size_t N>
using IndSeqFrom = decltype(getIndexSequence(std::make_index_sequence<N>{}));
template <typename T, std::size_t>
struct getTypeStruct
{ using type = T; };
template <typename T, std::size_t N>
using getType = typename getTypeStruct<T, N>::type;
template <std::size_t N = 50, typename = IndSeqFrom<N>>
struct foo;
template <std::size_t N, std::size_t ... Is>
struct foo<N, std::index_sequence<Is...>> : public foo<N-1u>
{
using foo<N-1u>::func;
static void func (getType<K, Is> ... ts)
{ std::cout << sizeof...(ts) << std::endl; }
};
template <>
struct foo<0, std::index_sequence<>>
{
static void func ()
{ std::cout << "0" << std::endl; }
};
int main()
{
foo<>::func({1,'2',3.0}); // print 1
foo<>::func({1,'2',3.0}, {4,'5',6.0}); // print 2
foo<>::func({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0}); // print 3
}
It's a pity you can't use C++17 because in you could use variadic unsing
and avoid at all recursive inheritance
#include <tuple>
#include <iostream>
#include <type_traits>
using K = std::tuple<int, char, double>;
template <std::size_t ... Is>
constexpr auto getIndexSequence (std::index_sequence<Is...> is)
-> decltype(is);
template <std::size_t N>
using IndSeqFrom = decltype(getIndexSequence(std::make_index_sequence<N>{}));
template <typename T, std::size_t>
struct getTypeStruct
{ using type = T; };
template <typename T, std::size_t N>
using getType = typename getTypeStruct<T, N>::type;
template <std::size_t N, typename = IndSeqFrom<N>>
struct bar;
template <std::size_t N, std::size_t ... Is>
struct bar<N, std::index_sequence<Is...>>
{
static void func (getType<K, Is> ... ts)
{ std::cout << sizeof...(ts) << std::endl; }
};
template <std::size_t N = 50, typename = IndSeqFrom<N>>
struct foo;
template <std::size_t N, std::size_t ... Is>
struct foo<N, std::index_sequence<Is...>> : public bar<Is>...
{ using bar<Is>::func...; };
int main()
{
foo<>::func({1,'2',3.0}); // print 1
foo<>::func({1,'2',3.0}, {4,'5',6.0}); // print 2
foo<>::func({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0}); // print 3
}
edited Dec 9 at 16:59
answered Dec 9 at 16:39
max66
33.9k63762
33.9k63762
add a comment |
add a comment |
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53691855%2fproblem-with-calling-a-variadic-function-template-when-passing-brace-initialiser%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
2
this, this or this
– Piotr Skotnicki
Dec 9 at 12:45
1
@PiotrSkotnicki That looks promising for the OP's requirement(C++11). Then why you don't paste them as an answer? (Just curious)
– JeJo
Dec 9 at 12:52
1
With different types in it,
{1, '2', 3.0}
can't be deduced asstd::initializer_list
or C-style array; and can't be deduced asstd::tuple<T, char, double>
because{1,
2, 3.0}
itself isn't astd::tuple
. I suppose you have to useK
, or explicit the type callingfoo()
(sofoo<int>( {1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0} );
) or avoid the braces, at least for the first triplet (sofoo(1,'2',3.0, {4,'5',6.0}, {7,'8',9.0})
) to permitT
deduction.– max66
Dec 9 at 13:13
1
@n.m. then what's wrong with providing a sufficient number of overloads like here ?
– Piotr Skotnicki
Dec 9 at 13:20
1
My third hypothesis before require and additional couple of braces:
foo(1,'2',3.0, {{4,'5',6.0}, {7,'8',9.0}})
. So the first1
is deduced asint
and the following triplets as astd::tuple<int, char, double>const [2]
– max66
Dec 9 at 13:20