Problem with std::index_sequence_for as default argument? - c++

The following C++20 program:
#include <utility>
#include <cstddef>
template<typename... Args>
class C {
template<size_t... I>
static void call(
std::index_sequence<I...> = std::index_sequence_for<Args...>{}
) {}
};
int main() {
C<long int>::call();
}
fails to compile with error message:
test.cc: In static member function ‘static void C<Args>::call(std::index_sequence<I ...>) [with long unsigned int ...I = {}; Args = {long int}; std::index_sequence<I ...> = std::integer_sequence<long unsigned int>]’:
test.cc:11:20: error: could not convert ‘std::index_sequence_for<long int>{}’ from ‘integer_sequence<[...],#‘nontype_argument_pack’ not supported by dump_expr#<expression error>>’ to ‘integer_sequence<[...],#‘nontype_argument_pack’ not supported by dump_expr#<expression error>>’
11 | C<long int>::call();
| ^
| |
| integer_sequence<[...],#‘nontype_argument_pack’ not supported by dump_expr#<expression error>>
test.cc:11:20: note: when instantiating default argument for call to ‘static void C<Args>::call(std::index_sequence<I ...>) [with long unsigned int ...I = {}; Args = {long int}; std::index_sequence<I ...> = std::integer_sequence<long unsigned int>]’
test.cc: In function ‘int main()’:
test.cc:11:20: error: could not convert ‘std::index_sequence_for<long int>{}’ from ‘integer_sequence<[...],#‘nontype_argument_pack’ not supported by dump_expr#<expression error>>’ to ‘integer_sequence<[...],#‘nontype_argument_pack’ not supported by dump_expr#<expression error>>’
Any ideas?
Update:
My current best workaround is to factor out default argument into two functions like:
template<typename... Args>
class C {
static void call() {
_call(std::index_sequence_for<Args...>{});
}
template<size_t... I>
static void _call(std::index_sequence<I...>) {}
};
This seems to work around compiler bug (if that's what it is).
Update 2:
The below program fails for the same reason the original one does:
template<typename T> void f(T x = 42) {}
int main() { f(); }
so it's a feature not a bug.

In general template argument deduction + default function arguments cause a lot of trouble. To simply fix it you can go with this:
#include <utility>
#include <cstddef>
template<typename... Args>
class C {
public:
static void call() {
call_impl(std::index_sequence_for<Args...>{});
}
private:
template<size_t... I>
static void call_impl(std::index_sequence<I...> ) {
}
};
int main() {
C<long int>::call();
}

In C++20 you can also write a templated lambda to do exactly that without creating a new function:
//...
static void call() {
return [&]<size_t... Is>(std::index_sequence<Is...>) {
/* your code goes here... */
}( std::index_sequence_for<Args...>{} );
}

Related

Why can't I deduce the function signature for a mutable lambda?

I have the following code which implements a memoize function.
note The question is not about how to write a memoize function specifically but rather about the compile error I get with this implementation and the smallest change to get it to work.
The implementation.
#include <functional>
#include <map>
#include <functional>
#include <iostream>
using namespace std;
template<typename T>
struct memfun_type
{
using type = void;
};
template<typename Ret, typename Class, typename... Args>
struct memfun_type<Ret(Class::*)(Args...) const>
{
using type = std::function<Ret(Args...)>;
};
template<typename F>
typename memfun_type<decltype(&F::operator())>::type
FFL(F const &func)
{ // Function from lambda !
return func;
}
template <typename ReturnType, typename... Args>
std::function<ReturnType (Args...)>
memoizeImp(std::function<ReturnType (Args...)> func)
{
std::map<std::tuple<Args...>, ReturnType> cache;
return ([=](Args... args) mutable {
std::tuple<Args...> t(args...);
if (cache.find(t) == cache.end())
cache[t] = func(args...);
return cache[t];
});
}
template <typename Fn>
auto memoize(Fn && fn){
return memoizeImp(FFL(fn));
}
and test program
int main()
{
auto a = 2.;
auto foo = [a](double x){return x+a;};
auto foom = memoize(foo);
std::cout << foo(1) << std::endl;
std::cout << foom(1) << std::endl;
}
the output as expected is
3
3
However if I make a small change to the test program changing
auto foo = [a](double x){return x+a;};
to
auto foo = [a](double x)mutable{return x+a;};
I get the following compile error on gcc
Could not execute the program
Compiler returned: 1
Compiler stderr
<source>: In instantiation of 'auto memoize(Fn&&) [with Fn = main()::<lambda(double)>&]':
<source>:49:24: required from here
<source>:42:26: error: invalid use of void expression
42 | return memoizeImp(FFL(fn));
| ~~~^~~~
<source>: In instantiation of 'typename memfun_type<decltype (& F::operator())>::type FFL(const F&) [with F = main()::<lambda(double)>; typename memfun_type<decltype (& F::operator())>::type = void; decltype (& F::operator()) = double (main()::<lambda(double)>::*)(double)]':
<source>:42:26: required from 'auto memoize(Fn&&) [with Fn = main()::<lambda(double)>&]'
<source>:49:24: required from here
<source>:24:12: error: return-statement with a value, in function returning 'memfun_type<double (main()::<lambda(double)>::*)(double)>::type' {aka 'void'} [-fpermissive]
24 | return func;
| ^~~~
The failing code and compile error can be viewed and tested at https://godbolt.org/z/74PKWvqr4
I'm not sure what the fix is to make it work with mutable lambdas.
You are lacking a specialisation.
Adding this makes it work
template<typename Ret, typename Class, typename... Args>
struct memfun_type<Ret(Class::*)(Args...)>
{
using type = std::function<Ret(Args...)>;
};
The operator() of the closure-type of a lambda is const qualified iff the lambda is not declared mutable

C++17: map type to integer value at compile time

I have the following types:
struct A { };
struct B { };
struct C { };
template <typename Class, uint16_t i>
struct def {
using message_type = Class;
static constexpr uint16_t tag = i;
};
and this tuple:
constexpr auto types = std::make_tuple(def<A, 1>(), def<B, 2>(), def<C, 3>());
Types A, B and C should be mapped to corresponding values (A -> 1 etc.). I want to create something (function, struct) that given object of one of these types will return proper value. I tried doing the following:
template <typename T>
struct gettag {
static decltype(T::tag) value(typename T::message_type const&) { return T::tag; }
};
template <typename... Args>
struct tagdb : public gettag<Args>... {
tagdb(std::tuple<Args...> const& t) { }
};
int main() {
tagdb t(types);
A a;
std::cout << t.value(a) << '\n';
}
This does not work, g++ claims that request for member value is ambiguous:
x.cc: In function ‘int main()’:
x.cc:29:17: error: request for member ‘value’ is ambiguous
29 | std::cout << t.value(a) << '\n';
| ^~~~~
x.cc:16:26: note: candidates are: ‘static decltype (T::tag) gettag<T>::value(const typename T::message_type&) [with T = def<C, 3>; decltype (T::tag) = const short unsigned int; typename T::message_type = C]’
16 | static decltype(T::tag) value(typename T::message_type const&) { return T::tag; }
| ^~~~~
x.cc:16:26: note: ‘static decltype (T::tag) gettag<T>::value(const typename T::message_type&) [with T = def<B, 2>; decltype (T::tag) = const short unsigned int; typename T::message_type = B]’
x.cc:16:26: note: ‘static decltype (T::tag) gettag<T>::value(const typename T::message_type&) [with T = def<A, 1>; decltype (T::tag) = const short unsigned int; typename T::message_type = A]’
I am a little surprised, especially since it clearly shows that each method is parameterized using different types.
Is there a way to make this solution work or should I completely change my approach? Note that what I want to avoid most is writing overloads for each type.
I suggest a solution without a std::tuple and gettag:
struct A { };
struct B { };
struct C { };
template <typename Class, std::uint16_t i>
struct def {
static constexpr std::uint16_t value(Class) {
return i;
}
};
template <typename... Tags>
struct tagdb : public Tags... {
using Tags::value...;
};
template<class... Tags>
constexpr auto make_tagdb(Tags...) {
return tagdb<Tags...>{};
}
// template<class... Tags>
// constexpr auto make_tagdb(std::tuple<Tags...>) {
// return tagdb<Tags...>{};
// }
constexpr auto tags = make_tagdb(def<A, 1>(), def<B, 2>(), def<C, 3>());
int main() {
A a;
std::cout << tags.value(a) << '\n'; // Output: 1
}
The problem is that you have template base classes, all of which declare a member with the same name. The easiest fix is just to pull all the base class value functions into the derived class:
using gettag<Args>::value...;
See https://godbolt.org/z/F_Prhg

Error storing instance of member function template

I'm trying to store a pointer to an instance of the member function template Derived::initialize as follows (see also rextester.com. For posterity, I've created a simpler version of the problem.):
class Base
{
public:
typedef void (Base::*setterFunction)( unsigned );
template<unsigned N>
struct SetterInterface
{
static Base::setterFunction Function;
};
protected:
template<unsigned N>
void setterImpl( unsigned )
{
}
};
template<unsigned N>
Base::setterFunction Base::SetterInterface<N>::Function = &Base::setterImpl<N>;
class Derived : public Base
{
public:
typedef void (Derived::*Initializer)();
template<typename T , void (T::*F)( unsigned ) >
void initialize()
{
}
template<typename C>
Derived( C* )
{
Initializer initializer = &Derived::initialize<C, C::template SetterInterface<0>::Function>; // NOT OK
//Initializer initializer = &Derived::initialize<C, C::template setterImpl<0> >; // OK
}
};
int main()
{
Derived derived( (Base*)0 );
}
But I'm getting the error message on GCC 5.4.0 (and 6.4.0)
Test.cpp: In instantiation of ‘Derived::Derived(C*) [with C = Base]’:
Test.cpp:45:28: required from here
Test.cpp:37:39: error: no matches converting function ‘initialize’ to type ‘Derived::Initializer {aka void (class Derived::*)()}’
Initializer initializer = &Derived::initialize<C, C::template SetterInterface<0>::Function>;
^
Test.cpp:30:7: note: candidate is: template<class T, void (T::* F1)(unsigned int)> void Derived::initialize()
void initialize()
The problem appears to lie with the member function template argument because C::template setterImpl<0> works whereas C::template SetterInterface<0>::Function (which I suppose to be an alias to the former) does not. For example:
Base::setterFunction f1 = &Base::setterImpl<0>;
Base::setterFunction f2 = Base::template SetterInterface<0>::Function;
The problem is that initialize() non-type-template-parameter should be a constant expression; in your code it's a static member instead, so it cannot work; it could, if you declared it as static constexpr ( but you'd need at least a C++11 compiler then ):
class Base
{
protected:
template<unsigned N>
void setterImpl( unsigned );
public:
typedef void (Base::*setterFunction)( unsigned );
template<unsigned N>
struct SetterInterface
{
static constexpr Base::setterFunction Function = &Base::setterImpl<N>;
};
};

GCC failed with variadic template and pointer to member function

#include <memory>
#include <iostream>
class Manager
{
public:
Manager() {}
virtual ~Manager() {}
int funcA(std::shared_ptr<int> a, float b) { return *a + b; }
int funcA(std::shared_ptr<double> a) { return *a; }
};
template <typename T, typename... Args>
auto resolver(int (Manager::*func)(std::shared_ptr<T>, Args...)) -> decltype(func) {
return func;
}
int main(int, char **)
{
Manager m;
Manager *ptr = &m;
auto var = std::make_shared<int>(1);
int result = (ptr->*resolver<int>(&Manager::funcA))(var, 2.0);
std::cout << result << std::endl;
return 0;
}
This code fail to compile with gcc but is fine with clang.
(gcc 5.3.1 and 6.0.0 20151220).
Do you know if there is any solution to make it compile with gcc ? I tried with template specialization and explicit instantiation.
EDIT: gcc gives the following error:
test > g++ -std=c++11 test.cpp
test.cpp: In function 'int main(int, char**)':
test.cpp:29:52: error: no matching function for call to 'resolver(<unresolved overloaded function type>)'
int result = (ptr->*resolver<int>(&Manager::funcA))(var, 2.0);
^
test.cpp:15:6: note: candidate: template<class T, class ... Args> decltype (func) resolver(int (Manager::*)(std::shared_ptr<_Tp1>, Args ...))
auto resolver(int (Manager::*func)(std::shared_ptr<T>, Args...)) -> decltype(func) {
^
test.cpp:15:6: note: template argument deduction/substitution failed:
test.cpp:29:52: note: mismatched types 'std::shared_ptr<int>' and 'std::shared_ptr<double>'
int result = (ptr->*resolver<int>(&Manager::funcA))(var, 2.0);
^
test.cpp:29:52: note: could not resolve address from overloaded function '& Manager::funcA'
test >
As a workaround, you may use
template <typename T>
struct resolver
{
template <typename... Args>
auto operator ()(int (Manager::*func)(std::shared_ptr<T>, Args...)) -> decltype(func) {
return func;
}
};
With call like
(ptr->*resolver<int>{}(&Manager::funcA))(var, 2.0);
Note the extra {} to call constructor.

I tried making a class which checks if the number of arguments of a function is valid, but it doesn't work

Here is code I made:
#include <iostream>
#include <algorithm>
template <class T, T(*func)(T ...)> class Foo {
public:
template <class ...Args> Foo(const Args &...args) {
func(static_cast<T>(args)...);
}
};
int main() {
Foo<int, &std::max>(0, 1);
return 0;
}
... So that Foo::Foo would throw error if the number of arguments is invalid.
But I get this error:
main.cpp: In function 'int main()':
main.cpp:10:23: error: could not convert template argument '& std::max' to 'int (*)(int, ...)
Foo<int, &std::max>(0, 1);
^
What is the problem?
I workarounded something. Don't really know if it's useful to you, but here we go:
#include <iostream>
#include <algorithm>
template <class T> class Foo {
public:
template <class ...Args>
const T& foo(const T & (*func)(const Args& ...), const Args &...args) {
return func(static_cast<T>(args)...);
}
};
int main() {
std::cout << Foo<int>().foo(&std::max<int>, 0, 1);
return 0;
}
It actually fails compilation when you do something like:
Foo<int>().foo(&std::max<int>, 0, 1, 3);
More generic and better (imho) solution(s)
#include <iostream>
#include <algorithm>
template <class T>
struct Foo {
template <class ...Args>
void check(T (func)(Args ...), Args&& ...args) {}
};
// maybe clearer
template<class T, class... Args>
void signatureChecker( T (func)(Args ...), Args&& ...args ) {}
void foo( int i, int x ) {}
int main() {
int t = 3;
const int& tt = t;
// non const version accepts a initializer list
Foo<int>().check( &std::max<int>, {3,3} );
Foo<int>().check( &std::max<int>, {tt,3} );
Foo<const int&>().check( &std::max<int>, tt, tt );
Foo<void>().check( &foo, 2, 2 );
signatureChecker<const int&>( &std::max<int>, tt, tt );
signatureChecker<int>( &std::max<int>, {3,3} );
return 0;
}
Another solution using types directly instead of parameters
#include <iostream>
#include <algorithm>
template <class RetType, class... Args>
struct CheckSignature {
template<RetType(func)(Args...)>
struct ForFunction {};
};
int main() {
CheckSignature<const int&, const int&, const int&>
::ForFunction<&std::max>();
return 0;
}
With all these examples and modifications of what you're trying to achieve, I think you can suit them to your needs.