While playing with variadic templates, classes, functions and lambdas, (from here) I found that following code is running with clang++ while not running with g++ :
#include <iostream>
#include <string>
using namespace std;
template <class... F>
struct overload_set : F...
{
overload_set(F... f) : F(f)... {}
};
template <class... F>
auto overload(F... f)
{
return overload_set<F...>(f...);
}
int main()
{
auto func = overload (
[](int &val) { val *= 2; },
[](string &arg) { arg += arg; },
[](char &c) { c = 'x'; }
);
int val = 10;
string str = "stackoverflow";
char ch = 's';
cout << val << " : " << str << " : " << ch << endl;
func(val);
func(str);
func(ch);
cout << val << " : " << str << " : " << ch << endl;
return 0;
}
For clang : coliru
For g++ : coliru
g++ is giving ambiguous operator() for func(val), func(str) and func(c). I think the operator() must not be ambiguous, as each one is having different arguments.
What's the problem with g++?
This has little to do with lambdas, variadic templates, operators or any advanced C++1{xy} stuff. Let's simplify:
struct foo
{
void func(int&){}
};
struct bar
{
void func(char&){}
};
struct test : foo, bar {};
int main()
{
test t;
int i = 1;
char c = 'a';
t.func(i);
t.func(c);
}
This fails to compile in either g++ or clang++. Which is a good thing too, because that's how the language is specified to work.
If we change func to operator(), g++ continues to reject the program but clang++ either accepts or rejects it, depending on how the operator is called:
t.operator()(c); // rejected
t(c); // accepted
Which looks like a clang bug to me.
In order to make the code above compile, a very small change is needed:
struct test : foo, bar {
using foo::func;
using bar::func;
};
Now I have no idea how to make pack expansion work in the using directive, or if it's indeed possible. But there's a workaround:
template <class... F> struct overload_set;
template <> struct overload_set<> {};
template <class F> struct overload_set<F> : F {
using F::operator();
overload_set(F f) : F(f) {}
};
template <class F, class... Fs>
struct overload_set<F, Fs...> : F, overload_set<Fs...>
{
overload_set(F f, Fs... fs) : F(f), overload_set<Fs...>(fs...) {}
using F::operator();
using overload_set<Fs...>::operator();
};
With this change your code compiles with both g++ and clang++.
Related
I had the bright idea that I could use concepts and structured bindings to figure out the number of fields in a struct and then dispatch based on this. The code we came up with is
#include <iostream>
#include <concepts>
template <typename T>
concept HasOneMember = requires (T const & t) {
[](T t){
auto [a]=t;
return 0;
}(t);
};
template <typename T>
concept HasTwoMember = requires (T const & t) {
[](T t){
auto [a,b]=t;
return 0;
}(t);
};
template <typename T> requires HasOneMember<T>
auto foo(T t)
{
auto & [a]=t;
std::cout << "has one member " << a << std::endl;
}
template <typename T> requires HasTwoMember<T>
auto foo(T t)
{
auto & [a,b]=t;
std::cout << "has not one member "<< a << " " << b << std::endl;
}
int main(){
struct One {
int a;
};
struct Two {
int b;
int c;
};
foo(One{7});
foo(Two{42, 8});
}
For clang this works
has one member 7
has not one member 42 8
is the output. However for gcc this does not.
https://godbolt.org/z/vdq6Koezc
I'm curious. Is this because the code is illegal and should not work or does GCC have a bug?
Maybe it is possible to tweak the code so it works in GCC also?
It also compiles for MSVC.
I'm trying to make my class be convertible to a function pointer, for a slew of reasons unrelated to this post.
When I try to do this with a non-template class, it works fine. Below, Bar bar; bar(1) correctly compiles, and segfaults as-expected. But Foo<int>; foo(1) doesn't compile at all.
I've tried multiple compilers, and I get: mismatched types 'Args' and 'int'
Any ideas? Live demo: https://wandbox.org/permlink/alSGBssfSd4pHgdl
#include <iostream>
#include <tuple>
using namespace std;
template<typename... Args>
using Test = void(*)(Args...);
template<typename T>
struct Foo {
template<typename... Args>
operator Test<Args...>() {
return Test<Args...>{};
}
};
struct Bar {
template<typename... Args>
operator Test<Args...>() {
return Test<Args...>{};
}
};
int main()
{
Foo<int> foo;
// foo(1);
Bar bar;
bar(1);
return 0;
}
Tried this awful syntax, too:
template<typename... Args>
(*operator void() const)(Args...) {
return {};
}
You can try this:
#include <iostream>
#include <tuple>
using namespace std;
template<typename... Args>
using Test = void(*)(Args...);
template<typename T>
struct Foo {
template<typename... Args>
operator Test<Args...>()
{
std::cout << __FUNCTION__ << std::endl;
return Test<Args...>{};
}
};
struct Bar {
template<typename... Args>
operator Test<Args...>()
{
std::cout << __FUNCTION__ << std::endl;
return Test<Args...>{};
}
};
int main()
{
Foo<int> foo;
auto x = static_cast<Test<int, double>>(foo);
Bar bar;
auto y = static_cast<Test<char, float>>(bar);
return 0;
}
When using Visual C++ 2019, I get the following run-time output:
Foo<int>::operator void (__cdecl *)(int,double)
Bar::operator void (__cdecl *)(char,float)
The use of the static_cast is to force the usage of the overloaded operator member functions.
Alternatively, you can also try:
#include <iostream>
#include <tuple>
#include <type_traits>
using namespace std;
template<typename... Args>
using Test = void(*)(Args...);
template<typename T>
struct Foo {
template<typename... Args>
Test<Args...> operator()(int x)
{
return Test<Args...>{};
}
};
struct Bar {
template<typename... Args>
Test<Args...> operator()(int x)
{
return Test<Args...>{};
}
};
int main()
{
Foo<int> foo;
auto w = foo.template operator()<int, double>(1);
std::cout << "w: " << typeid(w).name() << std::endl;
auto x = foo(2);
std::cout << "x: " << typeid(x).name() << std::endl;
Bar bar;
auto y = bar.template operator()<char, float>(3);
std::cout << "y: " << typeid(y).name() << std::endl;
auto z = bar(4);
std::cout << "z: " << typeid(z).name() << std::endl;
return 0;
}
When using Visual C++ 2019, I get the following run-time output:
w: void (__cdecl*)(int,double)
x: void (__cdecl*)(void)
y: void (__cdecl*)(char,float)
z: void (__cdecl*)(void)
In this way, the object is now callable and returns a function pointer.
This question already has an answer here:
Class Template Argument Deduction in member variables
(1 answer)
Closed 3 years ago.
Being inspired by This example, namely by this particular piece of code
// ...
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
// ...
std::visit(overloaded {
[](auto arg) { std::cout << arg << ' '; },
[](double arg) { std::cout << std::fixed << arg << ' '; },
[](const std::string& arg) { std::cout << std::quoted(arg) << ' '; },
}, v);
... I tried to define a class' member of type overloaded, but stumbled on difficulties, which I believe (but not exactly sure) have been caused by inability of the compiler to deduce template's parameters.
template<class ...Ts>
struct overloaded : Ts... {
using Ts::operator()...;
constexpr overloaded(Ts... ts) : Ts(ts)... {}
};
struct as{};
class phil_t {
int i;
overloaded o { [&](as&) {cout << i << "as"; } }; // <-- HERE
};
That's the output I receive:
../../variant.hpp:21:5: error: invalid use of template-name ‘overloaded’ without an argument list
overloaded o { [&](as&) {cout << i << "as"; } };
^~~~~~~~~~
Tellingly, when I use the same approach to instantiate an object not inside a class, everything works just smoothly.
void varnt() {
int i = 42;
auto o = overloaded( [&](as&) {cout << i << "as"; } );
}
Any advice, explaination or hint at some workarounds will be enormously appreciated.
Thank you.
You cannot have a member variable which is not of some specific type which is what you're trying to do in phil_t, as overloaded refers to a template and not a type.
class phil_t;
auto make_overloaded(phil_t& pt) {
return overloaded { [&](auto & x) { /* do something with x */ } };
}
class phil_t {
int i;
decltype(make_overloaded(std::declval<phil_t&>())) o = make_overloaded(*this); // <-- HERE
};
Live example here.
The problem with this code is that you want to use phil_t in your lambda, so you can't use my approach because phil_t is incomplete when make_overload is called.
Thanks to #Evg: here's a solution using type erasure that works better than the code above:
#include <iostream>
#include <any>
template<class ...Ts>
struct overloaded : Ts... {
using Ts::operator()...;
constexpr overloaded(Ts... ts) : Ts(ts)... {}
};
struct as1 {};
struct as2 {};
class phil_t {
private:
auto make_overloaded() {
return overloaded{
[&](as1& x) { std::cout << "1: " << i << std::endl; },
[&](as2& x) { std::cout << "2: " << i << std::endl; }
};
}
public:
phil_t() : o(make_overloaded()) {}
template<class As>
void foo(As as) {
std::any_cast<decltype(make_overloaded())>(o)(as);
}
private:
int i = 42;
std::any o;
};
int main() {
phil_t p;
p.foo(as1{});
p.foo(as2{});
}
All credits of this second snippet go to #Evg !
Live example here.
At the moment, I have the below working code where using class X, I provide a generic interface for multiple classes - I only expect a static f function to be present, but neither do I fix the return type nor the parameters:
#include <iostream>
#include <type_traits>
class A {
public:
static void f()
{
std::cout << "A::f()" << std::endl;
}
};
class B {
public:
static size_t f(std::string const& s_)
{
std::cout << "B::f()" << std::endl;
return s_.size();
}
};
class C {
};
template <typename T>
class X {
public:
static
void
f(...)
{
std::cout << "Operation not supported!" << std::endl;
}
template <typename... Args>
static
std::result_of_t<decltype(&T::f)(Args...)>
f(Args&&... args_)
{
return T::f(std::forward<Args>(args_)...);
}
};
int main()
{
X<A>::f(); // Compiles, but unexpected overload!
auto const x = X<B>::f("Hello"); // Works just fine
std::cout << x << std::endl;
// X<C>::f(); // Does NOT compile!
return 0;
}
However, I've got multiple problems with it (as marked above in the comments):
If I allow the function to be not present and uncomment the line
using C as a template parameter, the code does not compile, it
still expects C to have a function f:
In instantiation of ‘class X<C>’:
required from here
error: ‘f’ is not a member of ‘C’
std::result_of_t<decltype(&T::f)(Args...)>
Based on this, I expected the ellipsis parameter do the trick.
On the other hand, even if this worked, I would have another problem: though A provides f, the overload with
the ellipsis parameter is picked during overload resolution - where, of course, f provided by A is preferred.
(Used compiler: g++ (Ubuntu 8.1.0-5ubuntu1~16.04) 8.1.0; used standard: C++14.)
Any help solving the above problems (preferably both, at the same time) is welcomed.
I propose the following X struct
template <typename T>
struct X
{
static void g (...)
{ std::cout << "Operation not supported!" << std::endl; }
template <typename ... As, typename U = T>
static auto g(int, As && ... as)
-> decltype( U::f(std::forward<As>(as)...) )
{ return T::f(std::forward<As>(as)...); }
template <typename ... As>
static auto f (As && ... as)
{ return g(0, std::forward<As>(as)...); }
};
The points are
(1) Passing through a g() function with an additional unused parameter (int in the variadic version, adsorbed by ... in the "not supported version"), permit to select the variadic version (called with 0 that is an int), when sizeof...(As) == 0 and both g() functions are available. This because int is a better match (in an exact match) for int as .... But when the variadic version isn't available (see point (2)), the "non supported" version is still available (the only one available) and selected
(2) You have to SFINAE enable/disable the variadic version according the fact that the f() static method is (or ins't) available in T. Unfortunately T is a template argument of the class, not of the g() static method, so you can use it for SFINAE. The trick is SFINAE operate over an additional template parameter, U, that is defaulted to T
// ------------------------VVVVVVVVVVVVVV additional U (defaulted to T) template type
template <typename ... As, typename U = T>
static auto g(int, As && ... as)
-> decltype( U::f(std::forward<As>(as)...) )
// -------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//
// SFINAE enable this g() if (and only if) the call
// U::f(std::forward<As>(as)...) is valid
The following is a simplified working example
#include <iostream>
#include <type_traits>
struct A
{ static void f() { std::cout << "A::f()" << std::endl; } };
struct B
{
static size_t f(std::string const& s_)
{ std::cout << "B::f()" << std::endl; return s_.size(); }
};
struct C
{ };
template <typename T>
struct X
{
static void g (...)
{ std::cout << "Operation not supported!" << std::endl; }
template <typename ... As, typename U = T>
static auto g(int, As && ... as)
-> decltype( U::f(std::forward<As>(as)...) )
{ return T::f(std::forward<As>(as)...); }
template <typename ... As>
static auto f (As && ... as)
{ return g(0, std::forward<As>(as)...); }
};
int main ()
{
X<A>::f(); // now call variadic version
auto const x = X<B>::f("Hello"); // Works just fine as before
std::cout << x << std::endl;
X<C>::f(); // now compile
}
Note: I'm opening an issue to clang, but I would like to be sure that my code is valid too.
I was trying to reply to another answer and I found some difficulties while playing with lambdas and inheritance.
Consider the following, minimal example:
template<typename Func>
struct Base: Func {
Base(Func func): Func{func} {}
template<typename... Args>
auto operator()(Args... args)
-> decltype(Func::operator()(args...), void()) {
Func::operator()(args...);
}
};
int main() {
auto l = [](auto &&) {};
Base<decltype(l)> mixin{l};
mixin(0);
}
GCC 6.1 compiles it, clang 4.0 crashes.
Note that both compile just fine using the following definition:
auto l = [](int) {};
Is this valid code or I'm doing something that is not allowed by the standard?
Here is the link to the issue I've just opened.
Just if you need a solution for clang - the following code should work with clang
#include <utility>
#include <iostream>
template <typename F>
struct Base : F
{
Base (F f) : F {f} {}
template <typename... Args>
decltype(auto) operator () (Args&&... args)
{
std::cout << "(";
F::operator () (std::forward<Args> (args)...);
std::cout << ")" << std::endl;
}
};
int
main ()
{
auto l = [] (auto && i) {
std::cout << i;
};
Base<decltype(l)> mixin {l};
mixin (0);
return 0;
}