The following program does not compile:
#include <utility>
#include <iostream>
#define N 4
template <unsigned int I>
unsigned int g() { return I; }
template <unsigned int... I>
unsigned int f(std::integer_sequence<unsigned int, I...> = std::make_integer_sequence<unsigned int, N>{})
{
return (g<I>() + ...);
}
int main()
{
std::cout << f() << std::endl;
return 0;
}
Test it live on Coliru.
With gcc the error is
main.cpp: In function 'unsigned int f(std::integer_sequence<unsigned
int, I ...>) [with unsigned int ...I = {}]':
main.cpp:17:18: error: could not convert
'std::make_integer_sequence<unsigned int, 4>{}' from
'integer_sequence<[...],'nontype_argument_pack' not supported by
dump_expr>' to
'integer_sequence<[...],'nontype_argument_pack' not supported by
dump_expr>'
A similar conversion error is reported with clang++:
error: no viable conversion from 'std::make_integer_sequence<unsigned
int, 4>' (aka '__make_integer_seq<integer_sequence, unsigned int,
4U>') to 'std::integer_sequence'
Strangely enough, however, if I remove the default parameter, and pass the same expression to f, the program compiles and gives the corrected output:
#include <utility>
#include <iostream>
#define N 4
template <unsigned int I>
unsigned int g() { return I; }
template <unsigned int... I>
unsigned int f(std::integer_sequence<unsigned int, I...>)
{
return (g<I>() + ...);
}
int main()
{
std::cout << f(std::make_integer_sequence<unsigned int, N>{}) << std::endl;
return 0;
}
See it live on Coliru.
What's the problem/difference with the first code?
I admit, I do not understand the error message. The reason the second version compiles but not the first is that template parameters cannot be deduced from default arguments but from function parameters. Consider this simpler example:
#include <utility>
#include <iostream>
template <unsigned int>
struct foo {};
template <unsigned int x>
foo<x> make_foo(){ return {};}
template <unsigned int x>
unsigned int f(foo<x> = make_foo<4>())
{
return 42;
}
int main()
{
std::cout << f() << std::endl;
return 0;
}
Here the error is a little more descriptive:
<source>: In function 'int main()':
<source>:18:18: error: no matching function for call to 'f()'
18 | std::cout << f() << std::endl;
| ^
<source>:11:14: note: candidate: 'template<unsigned int x> unsigned int f(foo<x>)'
11 | unsigned int f(foo<x> = make_foo<4>())
| ^
<source>:11:14: note: template argument deduction/substitution failed:
<source>:18:18: note: couldn't deduce template parameter 'x'
18 | std::cout << f() << std::endl;
| ^
The main purpose of make_integer_sequence<unsigned int,N> is to make the transition from a single N to the pack in std::integer_sequence<unsigned int, I...> as you do it in your second example.
With a level of indirection you avoid that the caller must pass the parameter:
// ...
template <unsigned int... I>
unsigned int f(std::integer_sequence<unsigned int, I...>)
{
return (g<I>() + ...);
}
template <unsigned int X = N>
unsigned int f_wrap()
{
return f(std::make_integer_sequence<unsigned int,N>{});
}
int main()
{
std::cout << f_wrap() << std::endl;
return 0;
}
Live Demo
Related
Why this code doesn't compile ?
#include <iostream>
#include <typeinfo>
template <typename ...Ts>
void f();
template <typename T>
void f() {
std::cout << typeid(T).name() << std::endl;
}
template <typename T, typename U, typename ...Ts>
void f() {
std::cout << typeid(T).name() << ", ";
f<U, Ts...>();
}
int main(int argc, char** argv)
{
f<int, float, char>();
}
MSVC compiler error:
error C2668: 'f': ambiguous call to overloaded function
Expected output:
int, float, char
Side question: would there be a more modern way to do the same thing ?
EDIT
I've found a way to accept zero template pack:
#include <typeinfo>
#include <iostream>
#include <type_traits>
template <typename ...Ts>
using is_empty_pack = std::enable_if_t<sizeof ...(Ts) == 0>;
template <typename ...Ts, typename = is_empty_pack<Ts...>>
void f() {}
template <typename T, typename ...Ts>
void f() {
std::cout << typeid(T).name();
if constexpr (sizeof ...(Ts) > 0) std::cout << ", "; else std::cout << std::endl;
f<Ts...>();
}
int main(int argc, char *argv[])
{
f<>();
f<int>();
f<int, float>();
}
Any other suggestion?
Compiling with g++ gives a pretty clear explanation of what's happening:
prog.cc: In function 'int main(int, char**)':
prog.cc:20:24: error: call of overloaded 'f<int, float, char>()' is ambiguous
20 | f<int, float, char>();
| ~~~~~~~~~~~~~~~~~~~^~
prog.cc:5:6: note: candidate: 'void f() [with Ts = {int, float, char}]'
5 | void f();
| ^
prog.cc:13:6: note: candidate: 'void f() [with T = int; U = float; Ts = {char}]'
13 | void f() {
You've provided three different templated functions f, two of which could match what you've written here.
EDIT: Maybe you thought the first one was a declaration and the other two are specializations, but that's not how templates work. Specialization means specializing the type or value of a particular template argument, not specializing the number of template arguments.
Deleting
template <typename ...Ts>
void f();
will make the program compile and run with the expected behavior.
I am trying to write a variadic function template to calculate the byte size of a struct. This is going to be used in a network programming project I am working on. The first step I came up with this without the variadic template that works:
#include <cstdint>
#include <iostream>
struct Position
{
int32_t x;
int32_t y;
};
template<typename T>
inline std::size_t sizeT() { return sizeof(T); }
template<>
inline std::size_t sizeT<Position>() { return sizeT<int32_t>() + sizeT<int32_t>(); }
// if I change the definition of Position, I need to remember to change this function
int main(int argc, char* argv[])
{
std::cout << "int8_t:" << sizeT<int8_t>() << std::endl;
std::cout << "Position:" << sizeT<Position>() << std::endl;
return 0;
}
I compiled with g++ 9.3.0 on my Ubuntu 20.04 laptop and it worked fine. However if I try to make it a variadic template then I ran into different compilation errors. The point of this variadic template is to be able to write like the following, so that the implementation doesn't depend on the explicit knowledge of x and y's types.
Here is bad code #1
#include <cstdint>
#include <iostream>
struct Position
{
int32_t x;
int32_t y;
};
template<typename T>
inline std::size_t sizeT() { return sizeof(T); }
template<>
inline std::size_t sizeT<Position>()
{
return sizeT<decltype(Position::x), decltype(Position::y)>();
}
template<>
inline std::size_t sizeT<Position>() { return sizeT<int32_t>() + sizeT<int32_t>(); }
int main(int argc, char* argv[])
{
std::cout << "int8_t:" << sizeT<int8_t>() << std::endl;
std::cout << "Position:" << sizeT<Position>() << std::endl;
return 0;
}
I got
error: ambiguous template specialization 'sizeT' for 'std::size_t sizeT()
note: candidate are: 'template std::size_t sizeT()'
note: template<class T, class... Ts> std::size_t sizeT()'
Bad code #2. If I put the variadic template at a different location:
#include <cstdint>
#include <iostream>
struct Position
{
int32_t x;
int32_t y;
};
template<typename T>
inline std::size_t sizeT() { return sizeof(T); }
template<>
inline std::size_t sizeT<Position>() { return sizeT<int32_t>() + sizeT<int32_t>(); }
template<>
inline std::size_t sizeT<Position>()
{
return sizeT<decltype(Position::x), decltype(Position::y)>();
}
int main(int argc, char* argv[])
{
std::cout << "int8_t:" << sizeT<int8_t>() << std::endl;
std::cout << "Position:" << sizeT<Position>() << std::endl;
return 0;
}
I got
error: call of overloaded 'sizeT<int8_t>()' is ambiguous
note candidate: 'std::size_t sizeT() [with T=signed char; std::size_t = long unsigned int]'
note candidate: 'std::size_t sizeT() [with T=signed char; Ts={}; std::size_t = long unsigned int]'
error: call of overloaded 'sizeT()' is ambiguous
note candidate: 'std::size_t sizeT() [with T=Position; std::size_t = long unsigned int]'
note candidate: 'std::size_t sizeT() [with T=Position; Ts={}; std::size_t = long unsigned int]'
I kinda understand case #2 but I don't understand #1. How can I achieve this? Thank you so much in advance.
You don't use variadic...
You might do (C++17)
template <typename ... Ts>
constexpr std::size_t sizeT() { return (0 + ... + sizeof(Ts)); }
template <>
constexpr std::size_t sizeT<Position>()
{
return sizeT<decltype(Position::x), decltype(Position::y)>();
}
Demo
But sizeT<Position, Position> would return 2 * sizeof(Position) instead of 2 * sizeT<Position> (which are identical here).
You might do instead:
template <typename... Ts>
struct tag{};
template <typename T>
constexpr std::size_t sizeT(tag<T>) { return sizeof(T); }
template <typename ... Ts>
constexpr std::size_t sizeT(tag<Ts...>) { return (0 + ... + sizeT(tag<Ts>())); }
template <typename ... Ts>
constexpr std::size_t sizeT(tag<Ts>...) { return (0 + ... + sizeT(tag<Ts>())); }
constexpr std::size_t sizeT(tag<Position>)
{
return sizeT(tag<decltype(Position::x), decltype(Position::y)>());
}
Demo
I can't seem to figure out why the following code doesn't work:
#include <array>
template <long unsigned int s> void a() {}
template <long unsigned int s> void b(const std::array<int, s>& arr) {
a<arr.size()>(); // error: no matching function for call to 'a'
}
int main() {
const std::array<int, 2> arr {{0, 0}};
a<arr.size()>(); // Works
b<arr.size()>(arr);
return 0;
}
GCC fails with the following:
test.cpp: In instantiation of ‘void b(const std::array<int, s>&) [with long unsigned int s = 2]’:
test.cpp:13:22: required from here
test.cpp:6:18: error: no matching function for call to ‘a<(& arr)->std::array<int, 2>::size()>()’
6 | a<arr.size()>(); // Doesn't
| ~~~~~~~~~~~~~^~
test.cpp:3:37: note: candidate: ‘template<long unsigned int s> void a()’
3 | template <long unsigned int s> void a() {}
| ^
test.cpp:3:37: note: template argument deduction/substitution failed:
test.cpp:6:18: error: ‘arr’ is not a constant expression
6 | a<arr.size()>(); // Doesn't
| ~~~~~~~~~~~~~^~
test.cpp:6:15: note: in template argument for type ‘long unsigned int’
6 | a<arr.size()>(); // Doesn't
| ~~~~~~~~^~
I assume the ‘arr’ is not a constant expression part is most relevant, but I don't understand why the same line works in main() (is it a constant expression there?), and why passing arr as a const copy (rather than a reference) also resolves the issue.
PS: I know that I can just use a<s>();, but I'm just trying to figure out what this error means.
Maybe in C++2X, it will work with consteval functitons.
For non-reference argument it works https://godbolt.org/z/Wx9va54z5.
I think it is fine to pass std::array of built-ins by value in general anyway.
(This works in GCC and clang.)
#include <array>
template <long unsigned int s> void a() {}
template <long unsigned int s> void b(std::array<int, s> arr) {
a<arr.size()>(); // ok
}
int main() {
const std::array<int, 2> arr = {};
a<arr.size()>(); // Works
b<arr.size()>(arr);
return 0;
}
To answer the question in your comment #acumandr, yes, a static and constexpr size() member function in std::array would work(!), even for const& argument. https://godbolt.org/z/anP1e9qEr
CORRECTION: This is correct only for GCC, in clang doesn't work https://godbolt.org/z/65d5G9Yfo , Thanks #IlCapitano
#include <array>
template<class T, std::size_t D>
struct MyArray{
static constexpr std::size_t size(){return D;}
};
template <long unsigned int s> void a() {}
template <long unsigned int s> void b(MyArray<int, s> const& arr) {
a<arr.size()>(); // ok
}
int main() {
const MyArray<int, 2> arr = {};
a<arr.size()>(); // Works
b<arr.size()>(arr);
return 0;
}
which makes even more puzzling why std::array doesn't have a static (and constexpr) size member.
2.5) CORRECTION: This is correct only for GCC, in clang doesn't work https://godbolt.org/z/65d5G9Yfo , Thanks #IlCapitano
In clang, a static funciton works but not passing the instance, which kind of defeats the purpose:
https://godbolt.org/z/En13aEWPz
#include <array>
template<class T, std::size_t D>
struct MyArray{
static constexpr std::size_t size(){return D;}
};
template <long unsigned int s> void a() {}
template <long unsigned int s> void b(MyArray<int, s> const& arr) {
a<std::decay_t<decltype(arr)>::size()>(); // error: no matching function for call to 'a'
}
int main() {
const MyArray<int, 2> arr = {};
a<arr.size()>(); // Works
b<arr.size()>(arr);
return 0;
}
using std::tuple_size<decltype(...)> (or s itself) is a good workaround https://godbolt.org/z/K9xxKa1Px
#include <array>
template <long unsigned int s> void a() {}
template <long unsigned int s> void b(std::array<int, s> arr) {
a<std::tuple_size<decltype(arr)>::value /*or just s*/>(); // ok
}
int main() {
const std::array<int, 2> arr = {};
a<arr.size()>(); // Works
b<arr.size()>(arr);
return 0;
}
This program defines a simple container for an array of std::unique_ptr<unsigned int>.
#include <memory>
#include <array>
#include <type_traits>
template <unsigned int dim>
class container {
template <std::size_t... I>
container(std::array<unsigned int, dim> Ls, std::index_sequence<I...>) : container(std::get<I>(Ls)...) { }
public:
const std::array<std::unique_ptr<unsigned int>, dim> data;
template <typename... UInts, class = std::enable_if_t<(sizeof...(UInts) == dim) && (std::is_same_v<UInts, unsigned int> && ...)>>
container(UInts... Ls) : data{ std::make_unique<unsigned int>(Ls)...} { }
template <typename Indices = std::make_index_sequence<dim>>
container(std::array<unsigned int, dim> Ls) : container(Ls, Indices{}) { }
};
int main()
{
unsigned int x = 1;
unsigned int y = 2;
container<2> a({x,y});
return 0;
}
The compilation with gcc 8.2.1 fails however, with the following error.
$ g++ -Wall -pedantic -std=c++17 -o test test.cpp
test.cpp: In function ‘int main()’:
test.cpp:25:23: error: call of overloaded ‘container(<brace-enclosed initializer list>)’ is ambiguous
container<2> a({x,y});
^
test.cpp:17:3: note: candidate: ‘container<dim>::container(std::array<unsigned int, dim>) [with Indices = std::integer_sequence<long unsigned int, 0, 1>; unsigned int dim = 2]’
container(std::array<unsigned int, dim> Ls) : container(Ls, Indices{}) { }
^~~~~~~~~
test.cpp:6:7: note: candidate: ‘container<2>::container(const container<2>&)’ <deleted>
class container {
^~~~~~~~~
test.cpp:6:7: note: candidate: ‘container<2>::container(container<2>&&)’ <deleted>
make: *** [Makefile:3: test] Error 1
Indeed, the copy constructor is deleted because of the presence of the array of std::unique_ptr (but still participates to the overload resolution).
Strangely, on gcc 7.3.0 the above code compiles with no errors or warnings.
Also, defining a simplified container, having a copyable array of unsigned int, as in the following program, no error occurs with both gcc 8.2.1 and gcc 7.3.0
#include <array>
#include <type_traits>
template <unsigned int dim>
class container_simple {
template <std::size_t... I>
container_simple(std::array<unsigned int, dim> Ls, std::index_sequence<I...>) : container_simple(std::get<I>(Ls)...) { }
public:
const std::array<unsigned int, dim> data;
template <typename... UInts, class = std::enable_if_t<(sizeof...(UInts) == dim) && (std::is_same_v<UInts, unsigned int> && ...)>>
container_simple(UInts... Ls) : data{ Ls...} { }
template <typename Indices = std::make_index_sequence<dim>>
container_simple(std::array<unsigned int, dim> Ls) : container_simple(Ls, Indices{}) { }
};
int main()
{
unsigned int x = 1;
unsigned int y = 2;
container_simple<2> a({x,y});
return 0;
}
Is it a compiler bug? If not, where is the ambiguity?
What is wrong with the following piece of code?
template<typename X>
struct A {
template<int N>
int foo() const {
return N;
}
};
template<typename X>
struct B {
int bar(const A<X>& v) {
return v.foo<13>();
}
};
#include <iostream>
using std::cout;
using std::endl;
int main() {
A<double> a;
B<double> b;
cout << b.bar(a) << endl;
return 0;
}
Inside the function B::bar the compiler complains:
error: invalid operands of types
‘’ and ‘int’ to binary ‘operator<’
If A is not a template, everything compiles fine.
Change return v.foo<13>(); to return v.template foo<13>(); because foo is a dependent name and you need to mention that explicitly using .template construct.