In an answer to another question I posted, Jack Harwood shared a nice solution to detect empty variadic parameter packs using concepts. The example problem is to compute the number of parameter pack arguments using recursion. I reproduce his solution below.
template <typename... Args>
concept NonVoidArgs = sizeof...(Args) > 0;
template <typename... Args>
concept VoidArgs = sizeof...(Args) == 0;
template <VoidArgs...>
constexpr int NumArguments() {
return 0;
}
template<typename FirstArg, NonVoidArgs... RemainingArgs>
constexpr int NumArguments() {
return 1 + NumArguments<RemainingArgs...>();
}
Example:
int main() {
std::cout << NumArguments<int>() << std::endl; // 1
std::cout << NumArguments() << std::endl; // 0
std::cout << NumArguments<float, int, double, char>() << std::endl; // 4
return 0;
}
I think that this is a better solution than using class templates to specialize function templates. However, I'm not sure why it works. The template function
template<typename FirstArg, NonVoidArgs... RemainingArgs>
constexpr int NumArguments()
seems to require at least two template arguments. There needs to be a FirstArg and then at least 1 RemainingArgs. Why does the compiler call this overload(?) when there is only one template argument? Does this behavior create any problems with this solution?
The concepts themselves are correct but the problem is that the example uses them incorrectly and is itself faulty. In the given code the concept is imposed on an argument basis instead of the entire parameter pack.
Your current version
template<typename FirstArg, NonVoidArgs... RemainingArgs>
constexpr int NumArguments() {
return 1 + NumArguments<RemainingArgs...>();
}
is actually equivalent to a requires clause and a fold expression
template<typename FirstArg, typename... RemainingArgs>
int NumArguments() requires (NonVoidArgs<RemainingArgs> && ...) {
return 1 + NumArguments<RemainingArgs...>();
}
This actually means you are defining a function that must have an argument FirstArg and might have an additional parameter pack of arbitrary size RemainingArgs (including zero!). Each of these arguments - if present - is then checked independently if it fulfills the NonVoidArgs concept which of course is always true. This means the function basically degenerates to
template<typename FirstArg, typename... RemainingArgs>
int NumArguments() {
return 1 + NumArguments<RemainingArgs...>();
}
That being said it is actually not even necessary for the parameter pack to be NonVoidArgs: Try it here!
The correct way to impose restrictions not only on the data type of a single template argument but the entire parameter pack would actually be a requires clause as follows:
template<typename FirstArg, typename... RemainingArgs>
int NumArguments() requires NonVoidArgs<RemainingArgs...> {
return 1 + NumArguments<RemainingArgs...>();
}
As expected this would not work: Try it here! I guess the person who has written the code was just lucky it actually works as they did not enforce the concepts correctly. The basic idea behind the concept is flawed.
Related
Using C++ concepts, I'd like to be able to define concepts that force all the types in a parameter pack to either be a given type, or in a list of given types. The 4 concepts I've defined are:
type_is: check if type T matches type U
type_in: check if type T is one of the types in U...
all_types_are: check if all types in parameter pack T... match type U
all_types_in: check if all types in parameter pack T... are any of the types in U... (can be different types in U...)
The code, with a method for each concept to test agsinst:
#include <concepts>
#include <iostream>
#include <type_traits>
class A{public: int var = 100;};
class B{public: int var = 200;};
class C{public: int var = 300;};
template <typename TypeToCheck, typename TypeToCheckAgainst>
concept type_is = requires
{
requires std::same_as<std::remove_cvref_t<TypeToCheck>, TypeToCheckAgainst>;
};
template <typename TypeToCheck, typename ...TypesToCheckAgainst>
concept type_in = requires
{
requires (std::same_as<std::remove_cvref_t<TypeToCheck>, TypesToCheckAgainst> || ...);
};
template <typename ...TypesToCheck, typename TypeToCheckAgainst>
concept all_types_are = requires
{
requires (std::same_as<std::remove_cvref_t<TypeToCheckAgainst>, TypesToCheck> && ...);
};
template <typename ...TypesToCheck, typename ...TypesToCheckAgainst>
concept all_types_in = requires
{
requires (std::same_as<std::remove_cvref_t<TypesToCheck>, TypesToCheckAgainst> || ...);
};
auto method1(type_is<A> auto&& object)
{
std::cout << object.var;
std::cout << std::endl;
}
auto method2(type_in<A, B> auto&& object)
{
std::cout << object.var;
std::cout << std::endl;
}
auto method3(all_types_are<A> auto&&... objects)
{
(std::cout << ... << objects.var);
std::cout << std::endl;
}
auto method4(all_types_in<A, B> auto&&... objects)
{
(std::cout << ... << objects.var);
std::cout << std::endl;
}
int main()
{
A a;
B b;
C c;
method1(a);
method2(b);
method3(a, a, a);
method4(a, b, b);
return 0;
}
The first 2 concepts that check for a single type T work fine, but for the last 2 concepts, if I call methods constrained with those concepts, with arguments that should conform to the concepts, they still fail with:
main.cpp(75): error C2672: 'method3': no matching overloaded function found
main.cpp(75): error C7602: 'method3': the associated constraints are not satisfied
main.cpp(53): note: see declaration of 'method3'
main.cpp(76): error C2672: 'method4': no matching overloaded function found
main.cpp(76): error C7602: 'method4': the associated constraints are not satisfied
main.cpp(60): note: see declaration of 'method4'
I'm not sure how to correct my last two concepts so that they behave as desired, as I don't understand how the constraints aren't satisfied. What do I need to change in order to make the concepts work?
First, there is no need to use nested requires, just simple do
template <typename TypeToCheck, typename TypeToCheckAgainst>
concept type_is = std::same_as<
std::remove_cvref_t<TypeToCheck>, TypeToCheckAgainst>;
template <typename TypeToCheck, typename ...TypesToCheckAgainst>
concept type_in = (std::same_as<
std::remove_cvref_t<TypeToCheck>, TypesToCheckAgainst> || ...);
Second, you don't need to define all_types_are additionally, just reuse type_is/type_in for the variadic template version
auto method3(type_is<A> auto&&... objects) {
(std::cout << ... << objects.var);
std::cout << std::endl;
}
auto method4(type_in<A, B> auto&&... objects) {
(std::cout << ... << objects.var);
std::cout << std::endl;
}
Demo
Your interpretation of the syntax in
auto method3(all_types_are<A> auto&&... objects)
is wrong. Using a type constraint in a template parameter pack doesn't result in the constraint being checked once for the whole pack expansion.
Instead the constraint will be checked for each element of the pack individually. So all you need is
auto method3(type_is<A> auto&&... objects)
to achieve what you intent all_types_are to do.
It is not possible to use a type constraint to constraint multiple types at once. If you need this (which is not the case here) you will need to use a requires clause instead, e.g.:
auto method4(auto&&... objects)
requires all_types_in<decltype(objects)..., A, B>
{ /*...*/ }
However, this will fail here as well, because there is no way to decide which of the template arguments in all_types_in<decltype(objects)..., A, B> are supposed to be part of TypesToCheck and which are supposed to be part of TypesToCheckAgainst.
I want to calculate the sum of all arguments (non-type parameters) passed through a template. I compiled the following program with: g++ -std=c++17 -g -Wall -o main main.cpp. It seems that I miss something, because I get this errors when compiling:
error: call of overloaded ‘func<N_0>()’ is ambiguous
std::cout << func<N_0>() << std::endl;
error: call of overloaded ‘func<22>()’ is ambiguous
return N + func<Next... >();
#include <iostream>
using namespace std;
template <std::size_t T>
std::size_t sum()
{
return T;
}
template <std::size_t N, std::size_t ...Next>
std::size_t sum()
{
return N + sum<Next... >();
}
int main()
{
const size_t N_0 = 4;
const size_t N_1 = 11;
const size_t N_2 = 22;
std::cout << sum<N_0>() << std::endl;
std::cout << sum<N_0, N_1, N_2>() << std::endl;
return 0;
}
I found a lot of examples like this:
#include <iostream>
template<typename T>
T adder(T first) {
return first;
}
template<typename T, typename... Args>
T adder(T first, Args... args) {
return first + adder(args...);
}
int main() {
const int c = adder(1, 8, 4);
std::cout << c << '\n';
return 0;
}
I am very curious why my code is not working.
In both the first and second code example, calling the function with only one (template) argument results in both function templates being viable. The packs will simply be empty.
However, in overload resolution in the second example the variadic template is considered less specialized than the non-variadic one, basically because the set of possible arguments to call it with is a superset of those that the non-variadic one can be called with. This is only because of the parameter pack in the function parameters. Therefore overload resolution will prefer the non-variadic template as a tie-breaker.
This ordering doesn't apply to your first code example, where the function parameters of both templates are the same.
Therefore overload resolution with a single (template) argument is ambiguous in your first code example, but not ambiguous in the second.
You can specify that the variadic template requires at least two arguments to resolve the ambiguity:
template <std::size_t N, std::size_t M, std::size_t ...Next>
std::size_t sum()
{
// May wrap-around to zero
return N + sum<M, Next... >();
}
However, in C++17 and later the whole sum construct can be simplified by use of fold expressions to:
template <std::size_t ...Ns>
std::size_t sum()
{
// May wrap-around to zero
return (Ns + ...);
}
(This function should probably be marked noexcept and constexpr in C++17 or consteval in C++20 and one should be careful with many/large arguments since the addition will silently wrap-around if the sum becomes too large for std::size_t to hold.)
Suppose I want to define a C++ function that has no Input Parameters within bracket, but within the <> argument. I have a Parameter pack as Input Arguments. Meaning that I have to write, for example, a function
int calculate<args...>()
{
return 1 + calculate<some_arg,args...>();
}
And also a base case implementation I have to give. However, I am confronted with a lot of Compiler Errors, because I don't know how to write such a form of recursion out properly. What I have to write before the above function declaration?
template<int... args>
(if data type of args is int; any other data type is also possible in a similar way)? Or what I have to write to avoid Compiler Errors? I also tried
template<int some_arg,int... args>
But I don't know also how to deal with variadic templates (how to unpack them). Any help?
EDIT:
My attempt for one Special case
template<bool... dg>
int calculate<0>()
{
return 1;
}
Error message for this is:
error: expected initializer before ‘<’ token
But I don't know also how to deal with variadic templates (how to unpack them). Any help?
As of C++17 you needn't resort to recursion, but can use pack expansion:
#include <iostream>
template<int ...Args>
constexpr int calculate() {
return (Args + ...);
}
int main() {
std::cout << calculate<1, 2, 3>(); // 6
}
If you want to allow other types of non-type template parameters, you can make use of a placeholder type (auto) for non-type template parameters, also a C++17 feature:
template<auto ...Args>
constexpr auto calculate() {
return (Args + ...);
}
As you cannot partially specialize function templates, you will have to use delegation to a class template if you want to provide different implementations for different specializations:
#include <iostream>
#include <ios>
template<auto ...Args>
struct calculate_impl {
static constexpr auto calc() { return (Args + ...); }
};
template<bool ...Args>
struct calculate_impl<Args...> {
static constexpr bool calc() { return (Args && ...); }
};
template<auto ...Args>
constexpr auto calculate() {
return calculate_impl<Args...>::calc();
}
int main() {
std::cout << calculate<1, 2, 3>(); // 6
std::cout << std::boolalpha
<< "\n" << calculate<false,true>() // false
<< "\n" << calculate<true, true>(); // true
}
If you're using C++17+: refer to dfrib's answer
Here's how you would implement a function to add the elements of a parameter pack using template recursion
template<int arg>
constexpr int add()
{
return arg;
}
template<int arg1, int arg2, int... args>
constexpr int add()
{
return arg1 + add<arg2, args...>();
}
Regarding if you wanted to create a special case
template<int arg>
constexpr int calculate()
{
return arg;
}
template<> int calculate<0>() { return 1; } // special case
template<int arg1, int arg2, int... args>
constexpr int calculate()
{
return calculate<arg1>() + calculate<arg2,args...>();
}
This would make it to where every time you have a zero in you argument list, it will add 1 instead of 0
Another way to do recursion allows you to do it in a single function, utilizing the sizeof... operator:
template<int lhs, int... rhs>
int add()
{
if constexpr(sizeof...(rhs))
{
return lhs + add<rhs...>();
}
else
{
return lhs;
}
}
This also requires C++17 for if constexpr, but it can do things that fold expressions might not be able to.
I am trying to understand the C++11 feature called "variadic". Look at this simple code:
#include <iostream>
using namespace std;
template<typename T, typename... Args>
T adder(T first, Args... args) {
return first + adder(args...);
}
int main() {
int c = adder(1,8,4);
cout << c << endl;
cout << "Hello World!" << endl;
return 0;
}
Readig the c++ primer book I have understood that they work in recursive way and I also see that in the recursive call the args... part is passed.
I am using MinGW 5 and QtCreator to test this. Look
Ok, to fix the Too few arguments I could call adder(first, args...) but now the recursion is not proper and the program crashes. What to do? I cannot understand how to do this.
Looking online I've found an example like this
template <typename T, typename... Rest>
double sum(T t, Rest... rest) {
return t + sum(rest...);
}
It's basically the same. Do I have to put an explicit (non template) return type?
You need the "stop-recursion-case" (do not know the correct name now; UPDATE: it's called "base-case", thanks Quentin) with just one argument when the template function is unfolding.
#include <iostream>
template<typename T>
T adder(T first) {
return first;
}
template<typename T, typename... Args>
T adder(T first, Args... args) {
return first + adder(args...);
}
int main() {
const int c = adder(1, 8, 4);
std::cout << c << '\n';
return 0;
}
With C++17 fold expression you can do it with a single function.
You don't need the "stop-recursion-case".
template<typename... Args>
auto sum(Args... args)
{
return (args + ...);
}
Example usage, all print 42
std::cout << sum(42) << '\n';
std::cout << sum(11, 31.0) << '\n'; // different types
std::cout << sum(3, 4, 5, 6, 7, 8, 9) << '\n';
using namespace std::string_literals;
std::cout << sum("4"s, "2"s) << '\n'; // concatenating strings
Your recursion unfolds like this:
adder(1,8,4)
-> adder<int,int,int>(1,8,4)
-> 1 + adder<int,int>(8,4)
-> 1 + 8 + adder<int>(4)
-> 1 + 8 + 4 + adder<>()
so Args... is getting shorter every time, and eventually is empty.
But your declaration
template<typename T, typename... Args>
T adder(T first, Args... args);
can't be called with zero arguments, it always needs at least one (first).
So, the options are either
add an overload which does take zero arguments, like
int adder() { return 0; }
add an overload taking exactly one argument, which doesn't try to continue the recursion:
template <typename T>
T adder(T t) { return t; }
either one will fix the bug, but the second is much better, because it works for any T, and the first will only add things that are implicitly convertible from int{0}.
This pattern - the general recursive case plus the terminal case which stops recursion - was common before variadic templates were introduced (we previously used LISP-like recursive lists for this sort of thing, which naturally use recursion like this).
The newer style enabled by variadic templates takes advantage of pack expansion to avoid recursion entirely. See for example the summation example using std::apply
The answer from #christian-g is correct. However, I would like you to notice that this function can be generalised to any return type with auto keyword.
template<typename T1, typename T2>
auto adder(const T1& t1, const T2& t2) {
return t1 + t2;
}
template<typename T1, typename... T2>
auto adder(const T1& t1, const T2&... t2) {
return t1 + adder(t2...);
}
Now you can use it for different types
auto sum1 = adder(1, 2, 3); // sum1 is int
auto sum2 = adder(1, 2., 3); // sum2 is double
There was an issue I have run against once with concatenating values to the string result, but it was solved in type deduction from char array to std::string
A variadic template parameter like Args allows the case with sizeof...(Args)=0, which returns the number of variadic template arguments. Using C++11 you need to define a case that is valid for no variadic templates given like
template<typename _T>
_T adder(_T first) { return first; }
However considering different types like double and int it is not always meaningful to return _T, mayber you want to change it to auto or declytpe.
Additionally this problem is solved in C++17 if you are intreseted.
template<typename ...Args>
int sum(Args&&... args) {
return (args + ... + (1 * 2));
}
This used to work some weeks ago:
template <typename T, T t>
T tfunc()
{
return t + 10;
}
template <typename T>
constexpr T func(T t)
{
return tfunc<T, t>();
}
int main()
{
std::cout << func(10) << std::endl;
return 0;
}
But now g++ -std=c++0x says:
main.cpp: In function ‘constexpr T func(T) [with T = int]’:
main.cpp:29:25: instantiated from here
main.cpp:24:24: error: no matching function for call to ‘tfunc()’
main.cpp:24:24: note: candidate is:
main.cpp:16:14: note: template<class T, T t> T tfunc()
main.cpp:25:1: warning: control reaches end of non-void function [-Wreturn-type]
clang++ -std=c++11 says that template's parameters of tfunc<T, t>() are ignored because invalid.
Is that a bug, or a fix ?
PS:
g++ --version => g++ (GCC) 4.6.2 20120120 (prerelease)
clang++ --version => clang version 3.0 (tags/RELEASE_30/final) (3.0.1)
The parameter t is not a constant expression. Hence the error. It should be also noted that it cannot be a constant expression.
You can pass the constant expression as argument, but inside the function, the object (the parameter) which holds the value, is not a constant expression.
Since t is not a constant expression, it cannot be used as template argument:
return tfunc<T, t>(); //the second argument must be a constant expression
Maybe, you want something like this:
template <typename T, T t>
T tfunc()
{
return t + 10;
}
template <typename T, T t> //<---- t became template argument!
constexpr T func()
{
return tfunc<T, t>();
}
#define FUNC(a) func<decltype(a),a>()
int main()
{
std::cout << FUNC(10) << std::endl;
}
Now it should work : online demo
I get the feeling that constexpr must also be valid in a 'runtime' context, not just at compile-time. Marking a function as constexpr encourages the compiler to try to evaluate it at compile-time, but the function must still have a valid run-time implementation.
In practice, this means that the compiler doesn't know how to implement this function at runtime:
template <typename T>
constexpr T func(T t)
{
return tfunc<T, t>();
}
A workaround is to change the constructor such that it takes its t parameter as a normal parameter, not as a template parameter, and mark the constructor as constexpr:
template <typename T>
constexpr T tfunc(T t)
{
return t + 10;
}
template <typename T>
constexpr T func(T t)
{
return tfunc<T>(t);
}
There are three levels of 'constant-expression-ness':
template int parameter, or (non-VLA) array size // Something that must be a constant-expression
constexpr // Something that may be a constant-expression
non-constant-expression
You can't really convert items that are low in that list into something that is high in that list, but obviously the other route it possible.
For example, a call to this function
constexpr int foo(int x) { return x+1; }
isn't necessarily a constant-expression.
// g++-4.6 used in these few lines. ideone doesn't like this code. I don't know why
int array[foo(3)]; // this is OK
int c = getchar();
int array[foo(c)]; // this will not compile (without VLAs)
So the return value from a constexpr function is a constant expression only if all the parameters, and the implementation of the function, can be completed at executed at compile-time.
Recap the question: You have two functions which take a parameter of type T. One takes its parameter as a template parameter, and the other as a 'normal' parameter.
I'm going to call the two functions funcT and funcN instead of tfunc and func.
You wish to be able to call funcT from funcN. Marking the latter as a constexpr doesn't help.
Any function marked as constexpr must be compilable as if the constexpr wasn't there. constexpr functions are a little schizophrenic. They only graduate to full constant-expressions in certain circumstances.
It would not be possible to implement funcN to run at runtime in a simple way, as it would need to be able to work for all possible values of t. This would require the compiler to instantiate many instances of tfunc, one for each value of t. But you can work around this if you're willing to live with a small subset of T. There is a template-recursion limit of 1024 in g++, so you can easily handle 1024 values of T with this code:
#include<iostream>
#include<functional>
#include<array>
using namespace std;
template <typename T, T t>
constexpr T funcT() {
return t + 10;
}
template<typename T, T u>
constexpr T worker (T t) {
return t==0 ? funcT<T,u>() : worker<T, u+1>(t-1);
}
template<>
constexpr int worker<int,1000> (int ) {
return -1;
}
template <typename T>
constexpr T funcN(T t)
{
return t<1000 ? worker<T,0>(t) : -1;
}
int main()
{
std::cout << funcN(10) << std::endl;
array<int, funcN(10)> a; // to verify that funcN(10) returns a constant-expression
return 0;
}
It uses a function worker which will recursively convert the 'normal' parameter t into a template parameter u, which it then uses to instantiate and execute tfunc<T,u>.
The crucial line is return funcT<T,u>() : worker<T, u+1>(t-1);
This has limitations. If you want to use long, or other integral types, you'll have to add another specialization. Obviously, this code only works for t between 0 and 1000 - the exact upper limit is probably compiler-dependent. Another option might be to use a binary search of sorts, with a different worker function for each power of 2:
template<typename T, T u>
constexpr T worker4096 (T t) {
return t>=4096 ? worker2048<T, u+4096>(t-4096) : worker2048<T, u>(t);
}
I think this will work around the template-recursion-limit, but it will still require a very large number of instantiations and would make compilation very slow, if it works at all.
Looks like it should give an error - it has no way of knowing that you passed in a constant value as t to func.
More generally, you can't use runtime values as template arguments. Templates are inherently a compile-time construct.