It seems like this code is not correct as I get compiler errors for it. I'm trying to understand why:
template <class ... Ts>
struct type_list{};
template <class ... Ts, class T_, class ... Ts_>
auto foo(type_list<Ts...>, Ts&&..., T_&&t, Ts_&&...) {
return sizeof(T_);
}
int main() {
std::cerr << foo(type_list<int>{}, 5, 5.0, 3);
return 0;
}
clang produces the follow error:
example.cpp:16:16: error: no matching function for call to 'foo'
std::cerr << foo(type_list<int>{}, 5, 5.0, 3);
^~~
example.cpp:11:6: note: candidate template ignored: deduced conflicting types for parameter 'Ts'
(<int> vs. <>)
auto foo(type_list<Ts...>, Ts&&..., T_&&t, Ts_&&...) {
It seems to me that in my call Ts should be deduced to int (since that's the only way to make the first argument work out), and then everything else will be forced as a result. Why doesn't this work?
Clang deduces Ts from two conflicting sources: type_list<Ts...> and Ts&&.... Because you call foo with type_list<int>{}, Ts is first deduced as {int}. However, because the last argument Ts_&&... has the type of a parameter pack, it catches all that it can; in this case, the last two arguments (while T_&&t is passed the 5), which deduces Ts_ as {double, int} (and T_ as int). This leaves Ts with no argument, so it's deduced as {}.
Hence Clang's error message: Ts is deduced both as {} and {int} (the (<int> vs. <>) in the error message).
Note that GCC does things differently:
error: too few arguments to function 'auto foo(type_list, Ts&& ..., T_&&, Ts_&& ...) [with Ts = {int}; T_ = int; Ts_ = {double, int}]'
It tries to keep Ts deduced as {int} but still deduces Ts_ as {double, int}, and so there is no way to give the correct number of arguments.
To explain a bit further, consider the following:
template <class ... Ts>
struct type_list{};
template <class ... Ts>
auto foo(type_list<Ts...>, Ts...) {
return sizeof...(Ts);
}
int main() {
std::cerr << foo(type_list<int>{}, 5, 5.0, 3);
}
Clang correctly calls this out because the two deductions are conflicting:
note: candidate template ignored: deduced conflicting types for parameter 'Ts' (<int> vs. <int, double, int>)
Trouble is, you can't catch 2 of the arguments with another parameter pack, because it will either catch everything if placed after (which is your case), or nothing if placed before:
template <class ... Ts>
struct type_list{};
template <class... Ts, class... Ts_>
auto foo(type_list<Ts...>, Ts_..., Ts...) {
return sizeof...(Ts);
}
int main() {
std::cerr << foo(type_list<int>{}, 5, 5.0, 3);
}
Here, Clang produces the same error message because Ts is still deduced as both {int} from the type_list, and {int, double, int} by catching all the remaining arguments.
I think your only choices are to somehow deal with tuples or be more explicit with the call: foo<int>(type_list<int>{}, 5, 5.0, 3) (and you can probably remove the type_list in this case). That way, Ts isn't deduced anymore: you explicitly make it {int}.
C++ doesn't try every possibility and find the only one that is legal. It follows very specific rules, and if those rules don't work, it generates an error.
In general, a deduced ... pack must be last if it is deduced. Attempts to work around it will fail.
Here is a solution that avoids your problem:
template <class ... Ts, class...Us>
auto foo(type_list<Ts...>, Us&&...us) {
return [](Ts&&...ts, auto&& t, auto&&...) {
return sizeof(t);
}(std::forward<Us>(us)...);
}
int main() {
std::cerr << foo(type_list<int>{}, 5, 5.0, 3);
return 0;
}
Related
I'm writing a template wrapper function that can be applied to a functions with different number/types of arguments.
I have some code that works but I'm trying to change more arguments into template parameters.
The working code:
#include <iostream>
int func0(bool b) { return b ? 1 : 2; }
//There is a few more funcX...
template<typename ...ARGS>
int wrapper(int (*func)(ARGS...), ARGS... args) { return (*func)(args...) * 10; }
int wrappedFunc0(bool b) { return wrapper<bool>(func0, b); }
int main()
{
std::cout << wrappedFunc0(true) << std::endl;
return 0;
}
Now I want int (*func)(ARGS...) to also be a template parameter. (It's for performance reasons. I want the pointer to be backed into the wrapper, because the way I'm using it prevents the compiler from optimizing it out.)
Here is what I came up with (The only difference is I've changed the one argument into a template parameter.):
#include <iostream>
int func0(bool b) { return b ? 1 : 2; }
//There is a few more funcX...
template<typename ...ARGS, int (*FUNC)(ARGS...)>
int wrapper(ARGS... args) { return (*FUNC)(args...) * 10; }
int wrappedFunc0(bool b) { return wrapper<bool, func0>(b); }
int main()
{
std::cout << wrappedFunc0(true) << std::endl;
return 0;
}
This doesn't compile. It shows:
<source>: In function 'int wrappedFunc0(bool)':
<source>:9:55: error: no matching function for call to 'wrapper<bool, func0>(bool&)'
9 | int wrappedFunc0(bool b) { return wrapper<bool, func0>(b); }
| ~~~~~~~~~~~~~~~~~~~~^~~
<source>:7:5: note: candidate: 'template<class ... ARGS, int (* FUNC)(ARGS ...)> int wrapper(ARGS ...)'
7 | int wrapper(ARGS... args) { return (*FUNC)(args...) * 10; }
| ^~~~~~~
<source>:7:5: note: template argument deduction/substitution failed:
<source>:9:55: error: type/value mismatch at argument 1 in template parameter list for 'template<class ... ARGS, int (* FUNC)(ARGS ...)> int wrapper(ARGS ...)'
9 | int wrappedFunc0(bool b) { return wrapper<bool, func0>(b); }
| ~~~~~~~~~~~~~~~~~~~~^~~
<source>:9:55: note: expected a type, got 'func0'
ASM generation compiler returned: 1
<source>: In function 'int wrappedFunc0(bool)':
<source>:9:55: error: no matching function for call to 'wrapper<bool, func0>(bool&)'
9 | int wrappedFunc0(bool b) { return wrapper<bool, func0>(b); }
| ~~~~~~~~~~~~~~~~~~~~^~~
<source>:7:5: note: candidate: 'template<class ... ARGS, int (* FUNC)(ARGS ...)> int wrapper(ARGS ...)'
7 | int wrapper(ARGS... args) { return (*FUNC)(args...) * 10; }
| ^~~~~~~
<source>:7:5: note: template argument deduction/substitution failed:
<source>:9:55: error: type/value mismatch at argument 1 in template parameter list for 'template<class ... ARGS, int (* FUNC)(ARGS ...)> int wrapper(ARGS ...)'
9 | int wrappedFunc0(bool b) { return wrapper<bool, func0>(b); }
| ~~~~~~~~~~~~~~~~~~~~^~~
<source>:9:55: note: expected a type, got 'func0'
Execution build compiler returned: 1
(link to the compiler explorer)
It looks like a problem with the compiler to me, but GCC and Clang agree on it so maybe it isn't.
Anyway, how can I make this template compile correctly with templated pointer to a function?
EDIT:
Addressing the duplicate flag Compilation issue with instantiating function template
I think the core of the problem in that question is the same as in mine, however, it lacks a solution that allows passing the pointer to function (not only its type) as a template parameter.
This doesn't work because a pack parameter (the one including ...) consumes all remaining arguments. All arguments following it can't be specified explicitly and must be deduced.
Normally you write such wrappers like this:
template <typename F, typename ...P>
int wrapper(F &&func, P &&... params)
{
return std::forward<F>(func)(std::forward<P>(params)...) * 10;
}
(And if the function is called more than once inside of the wrapper, all calls except the last can't use std::forward.)
This will pass the function by reference, which should be exactly the same as using a function pointer, but I have no reasons to believe that it would stop the compiler from optimizing it.
You can force the function to be encoded in the template argument by passing std::integral_constant<decltype(&func0), func0>{} instead of func0, but again, I don't think it's going to change anything.
The 2nd snippet is not valid because:
a type parameter pack cannot be expanded in its own parameter clause.
As from [temp.param]/17:
If a template-parameter is a type-parameter with an ellipsis prior to its optional identifier or is a parameter-declaration that declares a pack ([dcl.fct]), then the template-parameter is a template parameter pack. A template parameter pack that is a parameter-declaration whose type contains one or more unexpanded packs is a pack expansion. ... A template parameter pack that is a pack expansion shall not expand a template parameter pack declared in the same template-parameter-list.
So consider the following invalid example:
template<typename... Ts, Ts... vals> struct mytuple {}; //invalid
The above example is invalid because the template type parameter pack Ts cannot be expanded in its own parameter list.
For the same reason, your code example is invalid. For example, a simplified version of your 2nd snippet doesn't compile in msvc.
I have this (supposedly not so useful) class template with templated constructor which is a candidate for perfect forwarding. I, however, wanted to make sure that the types passed to the constructor are the exact same as the ones specified for the whole class (without cvref qualifiers):
template <typename... Ts>
struct foo {
template <typename... CTs>
requires (std::same_as<std::remove_cvref_t<Ts>, std::remove_cvref_t<CTs>> && ...)
foo(CTs&& ...) {
std::cout << "called with " << (sizeof...(CTs)) << " args\n";
}
};
Now I can make:
auto f = foo<int, int>(1, 1);
and I can't make:
auto f = foo<float, int>(1, 1);
which is good.
But I wanted to extract the requires ... body to a concept:
template <typename... T1s, typename... T2s>
concept same_unqualified_types = (
std::same_as<
std::remove_cvref_t<T1s>,
std::remove_cvref_t<T2s>
> && ...
);
template <typename... Ts>
struct foo {
template <typename... CTs>
requires same_unqualified_types<Ts..., CTs...>
foo(CTs&& ...) { // 16
std::cout << "called with " << (sizeof...(CTs)) << " args\n";
}
};
int main() {
auto f = foo<int, int>(1, 1); // 22
}
But this gives me this error:
main.cpp: In function 'int main()':
main.cpp:22:32: error: no matching function for call to 'foo<int, int>::foo(int, int)'
22 | auto f = foo<int, int>(1, 1);
|
main.cpp:16:5: note: candidate: 'template<class ... CTs> requires same_unqualified_types<Ts ..., CTs ...> foo<Ts>::foo(CTs&& ...) [with CTs = {CTs ...}; Ts = {int, int}]'
16 | foo(CTs&& ...) {
| ^~~
main.cpp:16:5: note: template argument deduction/substitution failed:
main.cpp:16:5: note: constraints not satisfied
main.cpp: In substitution of 'template<class ... CTs> requires same_unqualified_types<Ts ..., CTs ...> foo<int, int>::foo(CTs&& ...) [with CTs = {int, int}]':
main.cpp:22:32: required from here
main.cpp:5:9: required for the satisfaction of 'same_unqualified_types<Ts ..., CTs ...>' [with CTs = {int, int}; Ts = {int, int}]
main.cpp:22:32: error: mismatched argument pack lengths while expanding 'same_as<typename std::remove_cvref<T1s>::type, typename std::remove_cvref<T2s>::type>'
22 | auto f = foo<int, int>(1, 1);
|
I suppose I might be doing something wrong with the concept same_unqualified_types where I'm trying to have two parameter packs. I've tried to test it manually, but it doesn't seem to work, even if I do same_unqualified_types<int, int> or same_unqualified_types<int, int, Pack...>, where Pack... is a parameter pack of two ints.
Where is my logic flawed? Can I extract that requires clause to a concept?
Disclaimer: I know I can achieve something similar with CTAD and deduction guides - without even needing any concepts. I just wish to know where my understanding is flawed.
Concepts don't get special privileges with regard to template parameter packs. You can't have two packs in a set of template parameters unless there's something to distinguish them in terms of the arguments passed to them (one could be a series of types while the other is a series of values, or you have access to template argument deduction to differentiate them, or something like that).
The best you're going to be able to do is to create an unqualified equivalent of same_as and just use that with pack expansion as needed:
template<typename T, typename U>
concept same_as_unqual = std::same_as<<std::remove_cvref_t<T>, <std::remove_cvref_t<U>>;
template <typename... Ts>
struct foo {
template <typename... CTs>
requires (same_as_unqual<Ts, CTs> && ...)
...
A single concept that does the pair-wise comparison between the parameters just isn't possible. Well, it's possible, but it'd be a lot more verbose, as you would likely need to bundle them in some kind of type-list. I don't think same_as_all<type_list<Ts...>, type_list<Us...>> is better than doing an explicit expansion.
I'm looking to make a loss function that can take 2 tensors of any dimensions as parameters, but the dimensions of tensor 1 (t1) and tensor (t2) must match. Below are the templates that I tried to use to can pass the tensors into the function. I was thinking that T would be a type and N would model the number of indexes possible without explicitly writing a type for infinitely possible tensor dimensions.
loss.h
#include <iostream>
namespace Loss {
template<class T, std::size_t N>
void loss(Eigen::Tensor<T, N, 0>& predicted, Eigen::Tensor<T, N, 0>& actual) {
std::cout << "Loss::loss() not implemented" << std::endl;
};
};
main.cpp
#include "loss.h"
int main() {
Eigen::Tensor<double, 3> t1(2, 3, 4);
Eigen::Tensor<double, 3> t2(2, 3, 4);
t1.setZero();
t2.setZero();
Loss::loss(t1, t2);
return 0;
}
The type error that I get before compiling from my editor:
no instance of function template "Loss::loss" matches the argument list -- argument types are: (Eigen::Tensor<double, 3, 0, Eigen::DenseIndex>, Eigen::Tensor<double, 3, 0, Eigen::DenseIndex>
And this is the message I get once I compile (unsuccessfully):
note: candidate template ignored: substitution failure [with T = double]: deduced non-type template argument does not have the same type as the corresponding template parameter ('int' vs 'std::size_t' (aka 'unsigned long'))
void loss(Eigen::Tensor<T, N, 0>& predicted, Eigen::Tensor<T, N, 0>& actual) {
^
1 error generated.
The error message is pointing out the type of the non-type template parameter is size_t, but in the declaration of t1 and t2 the value of that parameter is 3, which has type int. This mismatch makes the template argument deduction fail.
You can fix this by changing the type of the non-type template parameter to int
template<class T, int N>
void loss( // ...
or just let it be deduced
template<class T, auto N>
void loss( // ...
number literals are signed integers, and you’ve specified the number type of your template as size_t Which is unsigned. So the types don’t match. Try Eigen::Tensor<double, 3u> … in your main program to use unsigned literals.
This code below does not compile:
template<typename... Ts>
void CreateArr(const Ts&... args)
{
auto arr[sizeof...(args) + 1]{ args... };
}
int main()
{
CreateArr(1, 2, 3);
}
due to the following errors:
'arr': in a direct-list-initialization context, the type for 'auto [6]' can only be deduced from a single initializer expression
auto [6]': an array cannot have an element type that contains 'auto'
'initializing': cannot convert from 'const int' to 'std::initializer_list<int>'
My questions are:
Why cannot I use auto to define the type of the array?
How to define it properly to work with the template?
Why cannot I use auto to define the type of the array?
For the same reason, following does not work/ allowed!
auto ele[]{ 1, 2, 3 };
More reads: Why can't I create an array of automatic variables?
How to define it properly to work with the template?
Use the std::common_type_t for specifying the type
#include <type_traits> // std::common_type_t
template<typename... Ts>
void CreateArr(const Ts&... args)
{
std::common_type_t<Ts...> arr[sizeof...(args)]{ args... };
static_assert(std::is_array_v<int[sizeof...(args)]>, "is not array!");
}
(See a Live Demo)
The problem came from here - I wanted to create an approach solving a little bit more general problem. Consider an example:
#include <utility>
template<class T, std::size_t>
using deduced = T;
template<std::size_t N, class = std::make_index_sequence<N>>
struct Foo;
template<std::size_t N, std::size_t... Is>
struct Foo<N, std::index_sequence<Is...>>{
template <class... Args>
void Bar(deduced<Args, Is>...)
{ }
};
int main() {
Foo<3> myfoo;
myfoo.Bar(true, 2.0f, 3); // OK
//myfoo.Bar(1, 2, 3, 4); // error
}
clang has no problem with compiling the code, gcc on the other hand shows following errors:
prog.cc: In function 'int main()':
prog.cc:18:27: error: no matching function for call to 'Foo<3ul>::Bar(bool, float, int)'
myfoo.Bar(true, 2.0f, 3); // valid
^
prog.cc:12:10: note: candidate: template<class ... Args> void Foo<N, std::integer_sequence<long unsigned int, Is ...> >::Bar(deduced<Args, Is>...) [with Args = {Args ...}; long unsigned int N = 3ul; long unsigned int ...Is = {0ul, 1ul, 2ul}]
void Bar(deduced<Args, Is>...)
^~~
prog.cc:12:10: note: template argument deduction/substitution failed:
prog.cc:18: confused by earlier errors, bailing out
[live demo]
What confuses me gcc does not have a problem with deduction when using the same alias but outer parameter pack isn't involved, e.g.:
void Bar(deduced<Args, 0>...)
So the question is - is it legal to combine parameter packs from outer and inner class with alias of this form to make compiler deduce one of the template parameters or is it gcc bug?
Edit (based on bogdan's comment):
The code causes trouble also to MSVC (2017 RC), but works in this form with EDG compiler, icc (in version 16 and 17) also seem to deal well with a code. It is also worth noting that similar code with class instead of alias (also considered as deduced context in some places example by bogdan) causes even more trouble to compilers - only clang seems to deal well with this version(?)