boost::variant and function overload resolution - c++

The following code doesn't compile:
#include <boost/variant.hpp>
class A {};
class B {};
class C {};
class D {};
using v1 = boost::variant<A, B>;
using v2 = boost::variant<C, D>;
int f(v1 const&) {
return 0;
}
int f(v2 const&) {
return 1;
}
int main() {
return f(A{});
}
both gcc and clang complains with:
test1.cpp: In function ‘int main()’:
test1.cpp:18:17: error: call of overloaded ‘f(A)’ is ambiguous
return f(A{});
^
test1.cpp:18:17: note: candidates are:
test1.cpp:11:5: note: int f(const v1&)
int f(v1 const&) {
^
test1.cpp:14:5: note: int f(const v2&)
int f(v2 const&) {
Given that is not possible to construct a v2 object from an A instance why the compiler gives the same priority to both functions during the overload resolution?

The problem is the constructor
template<typename T>
variant(const T&)
of boost::variant. The code below reproduces the problem without all the magic:
struct C {};
struct A {
A(const C&) {}
template<typename T>
A(const T&) {}
};
struct B {
template<typename T>
B(const T&) {}
};
int f(const A&) {
return 0;
}
int f(const B&) {
return 1;
}
int main() {
return f(C{});
}
I think the variant constructor should only be enabled if the
argument is actually convertible to the arguments, you might want to
raise this as a bug.

A pragmatic approach, short of fixing the conversion with explicit conversion with proper SFINAE (I don't think it can be done elegantly outside the Boost Variant library), could be this:
See it Live On Coliru
#include <boost/variant.hpp>
class A {};
class B {};
class C {};
class D {};
using v1 = boost::variant<A, B>;
using v2 = boost::variant<C, D>;
namespace detail {
struct F : boost::static_visitor<int> {
template <typename... T>
int operator()(boost::variant<T...> const& v) const {
return boost::apply_visitor(*this, v);
}
int operator()(A) const { return 0; }
int operator()(B) const { return 0; }
int operator()(C) const { return 1; }
int operator()(D) const { return 1; }
} _f;
}
template <typename T>
int f(T const& t) {
return detail::F()(t);
}
int main() {
std::cout << f(A{}) << "\n";
std::cout << f(B{}) << "\n";
std::cout << f(C{}) << "\n";
std::cout << f(D{}) << "\n";
std::cout << f(v1{}) << "\n";
std::cout << f(v2{}) << "\n";
}
Prints
0
0
1
1
0
1
The assumption is that f(T) always returns the same value for the same T even if it is the member of more than one variant

Related

How to use `std::is_enum` with unnamed `enum`s?

The title is pretty much self explanatory. Here is my situation:
#include <type_traits>
class C1{
enum{
c1 = 3
}
}
class C2{
enum{
c2 = 10
}
}
template<class C>
class C3{
void do_this();
void do_that();
void foo(){
if constexpr(std::is_enum<C::c1>::value){
do_this();
}
if constexpr(std::is_enum<C::c2>::value){
do_that();
}
}
}
If I'd try to compile this I'd get the error
error: type/value mismatch at argument 1 in template parameter list for ‘template<class _Tp> struct std::is_enum’
note: expected a type, got ‘typename C::c1’
error: type/value mismatch at argument 1 in template parameter list for ‘template<class _Tp> struct std::is_enum’
note: expected a type, got ‘typename C::c2’
So hence my question: is it possible to use std::is_enum with unnamed enums?
C++11 using SFINAE
You can use decltype to get the type associated with c1 and c2 and then use SFINAE as shown below . C++11 Demo:
struct C1{
enum{
c1 = 3
};
};
struct C2{
enum{
c2 = 10
};
};
template<class C>
class C3{
void do_this(){std::cout << "do this called" << std::endl;}
void do_that(){std::cout << "do that called " << std::endl;}
public:
//overload for calling do_this
template<typename T = C,typename std::enable_if<std::is_same<T, C1>::value, bool>::type = std::is_enum<decltype(T::c1)>::value >void foo()
{
do_this();
}
//overload for calling do_that
template<typename T = C,typename std::enable_if<std::is_same<T, C2>::value, bool>::type = std::is_enum<decltype(T::c2)>::value >void foo()
{
do_that();
}
//overload when none of the conditions are satisfied
template<typename... T>
void foo(T...)
{
std::cout <<"ellipsis called " << std::endl;
}
};
int main()
{
C3<C1> c1;
c1.foo(); //calls do_this() using #1
C3<C2> c2;
c2.foo(); //calls do_that() using #2
C3<int> c3;
c3.foo(); //calls the ellipsis version
}
See also C++ 17 demo version that uses std::enable_if_t and std::is_same_v and std::is_enum_v.
C++20 using concept
struct C1{
enum{
c1 = 3
};
};
template<typename T>
concept enumC1 = std::is_same_v<T, C1>;
struct C2{
enum{
c2 = 10
};
};
template<typename T>
concept enumC2 = std::is_same_v<T, C2>;
template<class C>
class C3{
void do_this(){std::cout << "do this called" << std::endl;}
void do_that(){std::cout << "do that called " << std::endl;}
public:
//overload when none of the conditions are satisfied
template<typename T = C>
void foo()
{
std::cout <<"general called " << std::endl;
}
//overload for calling do_this
template<enumC1 T = C>void foo()
{
do_this();
}
//overload for calling do_that
template<enumC2 T = C>void foo()
{
do_that();
}
};
int main()
{
C3<C1> c1;
c1.foo(); //calls do_this()
C3<C2> c2;
c2.foo(); //calls do_that()
C3<int> c3;
c3.foo(); //calls the general version
}
C++ 20 demo
You should probably name the them both the same thing so the template works with both of them:
#include <cstdio>
#include <type_traits>
struct C1 {
enum { c = 3 };
};
struct C2 {
static constexpr int c = 10;
};
template <class C>
void foo() {
if constexpr (std::is_enum_v<decltype(C::c)>) {
std::puts("::c is an enum");
} else {
std::puts("::c is not an enum");
}
}
int main() {
foo<C1>();
foo<C2>();
}

Non-type template parameter with default value of type template parameter

I have the following code:
#include <iostream>
#include <string>
#include <type_traits>
struct Foo
{
int i;
int j;
};
template<typename T, T DEFAULT>
class Bar
{
public:
Bar(): mVal(DEFAULT)
{
std::cout << "Bar constructor with mVal = " << mVal << "\n";
}
~Bar(){}
Bar(const T &i) : mVal(i)
{
std::cout << "Bar constructor with mVal = " << mVal << "\n";
}
Bar &operator=(T const &val)
{
mVal = val;
std::cout << "Bar assignment operator with mVal = " << mVal << "\n";
return *this;
}
explicit operator T() const
{
return mVal;
}
private:
T mVal;
};
int main()
{
std::cout << "Hello \n";
Bar<int, 10> bar1;
}
This is working fine in gcc C++14 as long as the first template parameter in Bar is of an integral type. If I want to do Bar<Foo, {}> the following error message is printed:
on-type template parameters of class type only available with '-std=c++2a' or '-std=gnu++2a'
I already expected that. Changing template<typename T, T DEFAULT> class Bar to template<typename T, T DEFAULT = {}> class Bar leads to the same error.
Also a template specialization template<typename T> class Bar<T, {}> does not work for the same reason.
I also tried to experiment with std::enable_if_t<std::is_integral<T>::value> but could not find a solution that would work.
Is there any possible way to just write Bar<Foo> and not have to write a separate class like template<typename T, T DEFAULT> class BarDefault and template<typename T> class Bar for it?
Template parameters and template arguments - cppreference.com
A non-type template parameter must have a structural type, which is one of the following types (optionally cv-qualified, the qualifiers are ignored):
lvalue reference type (to object or to function);
an integral type;
a pointer type (to object or to function);
a pointer to member type (to member object or to member function);
an enumeration type;
std::nullptr_t; (since C++11)
a floating-point type;
a literal class type with the following properties:
all base classes and non-static data members are public and non-mutable and
the types of all bases classes and non-static data members are structural types or (possibly multi-dimensional) array thereof. (since C++20)
So basically custom structure as template value parameter is available since c++20.
Demo
You can overcome this problem by providing depending template which job is to provide a default value:
https://godbolt.org/z/RFp_xH
#include <iostream>
#include <string>
#include <type_traits>
struct Foo
{
int i = 42;
int j = 4;
};
std::ostream& operator<<(std::ostream& out, const Foo& a)
{
return out << a.i << ',' << a.j;
}
template<typename T>
struct BarDefaultValue
{
constexpr static T value()
{
return T{};
}
};
template<>
struct BarDefaultValue<int>
{
constexpr static int value()
{
return 42;
}
};
template<typename T, typename D = BarDefaultValue<T>>
class Bar
{
public:
Bar(): mVal(D::value())
{
std::cout << "Bar constructor with mVal = " << mVal << "\n";
}
~Bar(){}
Bar(const T &i) : mVal(i)
{
std::cout << "Bar constructor with mVal = " << mVal << "\n";
}
Bar &operator=(T const &val)
{
mVal = val;
std::cout << "Bar assignment operator with mVal = " << mVal << "\n";
return *this;
}
explicit operator T() const
{
return mVal;
}
private:
T mVal;
};
int main()
{
std::cout << "Hello \n";
Bar<int> bar1;
Bar<Foo> bar2;
}
You could make a default value to supply.
template<class T>
constexpr T brace_init_value{};
Then used as:
template<typename T, T DEFAULT = brace_init_value<T> >
class Bar
{
Thanks to #Marek R for his idea.
My solution to the problem is the following code which is compiling with gcc in C++14:
#include <iostream>
#include <string>
#include <type_traits>
template <typename T>
constexpr typename std::enable_if<std::is_class<T>::value, T>::type BarDefaultValue(const int &)
{
return {};
}
template <typename T>
constexpr typename std::enable_if<std::is_integral<T>::value, T>::type BarDefaultValue(const int &ret)
{
return static_cast<T>(ret);
}
struct Foo
{
int i;
int j;
};
std::ostream& operator<<(std::ostream& out, const Foo& a)
{
return out << a.i << ',' << a.j;
}
template<typename T, int DEFAULT = 0>
class Bar
{
public:
Bar(): mVal(BarDefaultValue<T>(DEFAULT))
{
std::cout << "Bar constructor with mVal = " << mVal << "\n";
}
~Bar(){}
Bar(const T &i) : mVal(i)
{
std::cout << "Bar constructor with mVal = " << mVal << "\n";
}
Bar &operator=(T const &val)
{
mVal = val;
std::cout << "Bar assignment operator with mVal = " << mVal << "\n";
return *this;
}
explicit operator T() const
{
return mVal;
}
private:
T mVal;
};
int main()
{
std::cout << "Hello \n";
Bar<int, 10> bar1;
Bar<Foo> bar2;
}
The output of this file is:
Hello
Bar constructor with mVal = 10
Bar constructor with mVal = 0,0

Why can't the compiler deduce the return type if argument type depends on a template parameter

I am getting a template deduction error when trying to choose a function of an overload set (foo) within a template based on another template parameter:
#include <iostream>
#include <string>
void foo(int a){
std::cout << "int";
}
void foo(double a) {
std::cout << "double";
}
template <typename T, typename R>
void print(const T& data, R(*fun)(typename T::type) ) {
fun(data.value);
}
struct IntData{
using type = int;
type value;
};
int main()
{
IntData x{1};
print(x, foo);
}
I get the following error:
In function 'int main()':
27:15: error: no matching function for call to 'print(IntData&, <unresolved overloaded function type>)'
27:15: note: candidate is:
15:6: note: template<class T, class R> void print(const T&, R (*)(typename T::type))
15:6: note: template argument deduction/substitution failed:
27:15: note: couldn't deduce template parameter 'R'
As template deduction should proceed from left to right, my assumption was that once the type of T is deduced, the type of R should also be deducable. Actually, I can get rid of the error when calling print like
print<IntData>(x, foo);
which seems to show that R can actually be deduced once T is known. So why doesn't it work if both parameter should be deduced?
Thank you!
I believe it is because you have R as the return type for your function pointer argument.
Note this quote from a previous question
So, when we ask about the signature of a function, we have to give two answers:
For functions that are specializations of function templates, the
signature includes the return type.
For functions that are not specializations, the return type is not part of the signature.
Since foo is simply an overloaded function and void is not part of the foo function signature, R will not assist the compiler in deducing correct function overload. Therefore, the use of foo as a function pointer is ambiguous within the scope of main. The compiler usually resolves the overload by matching the types of the provided arguments, for which there are none when the function pointer is by itself.
I believe this is the most robust solution, to include an intermediary function to resolve the previously ambiguous function pointer. I included some other types in addition to int to demonstrate the flexibility of using auto with the strategies mentioned below.
#include <iostream>
#include <string>
void foo(int a){
std::cout << "int" << std::endl;
}
void foo(double a) {
std::cout << "double" << std::endl;
}
bool foo(char a) {
std::cout << "char" << std::endl;
return true;
}
template <typename T, typename R>
R print(const T& data, R(*fun)(typename T::type) ) {
return fun(data.value);
}
struct IntData{
using type = int;
type value;
};
struct DoubleData{
using type = double;
type value;
};
struct CharData{
using type = char;
type value;
};
template <typename T>
auto print2(const T& data)
{
auto(*fooResolved)(typename T::type) = foo;
return print(data,fooResolved);
}
int main()
{
IntData x{1};
print2(x);
DoubleData y{1.0};
print2(y);
CharData z{'a'};
bool result = false;
std::cout << "bool before: " << result << std::endl;
result = print2(z);
std::cout << "bool after : " << result << std::endl;
}
Here are a few more potential solutions to help illustrate the problem:
(note the change is removing R as the second template argument)
#include <iostream>
#include <string>
void foo(int a){
std::cout << "int";
}
void foo(double a) {
std::cout << "double";
}
template <typename T>
void print(const T& data, void(*fun)(typename T::type) ) {
fun(data.value);
}
struct IntData{
using type = int;
type value;
};
int main()
{
IntData x{1};
print(x, foo);
}
As well as this (passing the value directly, which allows for multiple return types)
#include <iostream>
#include <string>
void foo(int a){
std::cout << "int";
}
void foo(double a) {
std::cout << "double";
}
template <typename T, typename R>
void print(const T& data, R (*fun)(T) ) {
fun(data);
}
struct IntData{
using type = int;
type value;
};
int main()
{
IntData x{1};
print(x.value, foo);
}
And to further illustrate the original issue (see the return type is now deduced)
#include <iostream>
#include <string>
void foo(int a){
std::cout << "int" << std::endl;
}
bool foo(double a) {
std::cout << "double" << std::endl;
return true;
}
template <typename T, typename R>
R print(const T& data, R (*fun)(T) ) {
return fun(data);
}
struct IntData{
using type = int;
type value;
};
struct DoubleData{
using type = double;
type value;
};
int main()
{
IntData x{1};
print(x.value, foo);
//foo(int) does not return a value
//bool test = print(x.value, foo); // Does not compile
DoubleData y{1.0};
bool result = false;
result = print(y.value, foo);
std::cout << result << std::endl;
}
And while we're at it, you could also resolve them given the original code by explicitly specifying which foo you want
#include <iostream>
#include <string>
void foo(int a){
std::cout << "int";
}
void foo(double a) {
std::cout << "double";
}
template <typename T, typename R>
void print(const T& data, R(*fun)(typename T::type) ) {
fun(data.value);
}
struct IntData{
using type = int;
type value;
};
int main()
{
IntData x{1};
void(*fooResolved)(int) = foo;
print(x, fooResolved);
}

Check for function existance on other type using C++ concepts

Does anybody know how to make a C++ concept T such that the function g is only defined for arguments t with type T if there exist an overload of f in B that accepts an argument t?
struct A1 {};
struct A2 {};
struct B {
void f(A1 a1) {}
};
void g(T t) {
B b;
b.f(t);
}
As an example, I want to define a to_string for everything that std::stringstream accepts, and define something like
std::string to_string(T t) {
std::stringstream ret;
ret << t;
return ret.str();
}
All examples on concepts deal with the easier case of requiring the existance of a function on a type, while in this case we want to check existance of a function on another type.
If you want to check if the type is streamable or not, you can have something like:
#include <iostream>
#include <concepts>
#include <sstream>
template <typename T>
concept Streamable = requires (T x, std::ostream &os) { os << x; };
struct Foo {};
struct Bar {};
std::ostream& operator<<(std::ostream& os, Foo const& obj) {
// write obj to stream
return os;
}
template <Streamable T>
std::string to_string(T t) {
std::stringstream ret;
ret << t;
return ret.str();
}
int main() {
Foo f;
Bar b;
to_string(f);
to_string(b); // error
return 0;
}
Demo
You can use two different type placeholders in a single concept, to require both the existence of a member function for an instance of one of the type placeholders, as well as the argument to said member function to match the type of another placeholder. E.g.:
#include <iostream>
template<typename T, typename U>
concept HasMemFnConstFoo = requires(const T t, const U u) {
t.foo(u);
};
template<typename U>
struct Bar {
template <typename T>
static void bar(const T& t)
{
if constexpr (HasMemFnConstFoo<T, U>) { t.foo(U{}); }
else { std::cout << "foo() not defined\n"; }
}
};
struct A1 {};
struct A2 {};
struct B1 {
void foo(const A1&) const { std::cout << "B1::foo()\n"; }
};
struct B2 {
void foo(const A1&) { std::cout << "B2::foo()\n"; }
};
struct B3 {
void foo(A1&) const { std::cout << "B3::foo()\n"; }
};
int main() {
Bar<A1>::bar(B1{}); // B1::foo()
Bar<A2>::bar(B1{}); // foo() not defined
Bar<A1>::bar(B2{}); // foo() not defined [note: method constness]
Bar<A2>::bar(B2{}); // foo() not defined
Bar<A1>::bar(B3{}); // foo() not defined [note: argument constness]
Bar<A2>::bar(B3{}); // foo() not defined
}

Overloading c++ template class method

Can I overload a template class function in a class that extends its specialization?
I have the following piece of code (I've tried to simplify it to the bare minimum):
#include <iostream>
using namespace std;
class X {
public:
unsigned test_x() {
return 1;
}
};
class Y {
public:
unsigned test_y() {
return 2;
}
};
template <typename T, typename U>
class A {
public:
unsigned foo(U i) {
cout << "A" << endl;
return i.test_x();
}
unsigned bar(T i) {
return foo(i);
}
};
class B : public A<Y, X> {
public:
unsigned foo(Y i) {
cout << "B" << endl;
return i.test_y();
}
};
int main() {
B b = B();
Y y = Y();
cout << "Hello: " << b.bar(y) << endl;
return 0;
}
However the compiler produces the following error:
hello.cc: In member function ‘unsigned int A<T, U>::bar(T) [with T = Y, U = X]’:
hello.cc:47: instantiated from here
hello.cc:30: error: no matching function for call to ‘A<Y, X>::foo(Y&)’
hello.cc:24: note: candidates are: unsigned int A<T, U>::foo(U) [with T = Y, U = X]
Basically I would like to overload the function A::foo() in its derived class B.
Apparently what you're asking is called "static polymorphism" and it's achieved by the means of "curiously recurring template pattern":
template <typename Derived, typename T, typename U>
class A {
public:
unsigned foo(U i) {
cout << "A" << endl;
return i.test_x();
}
unsigned bar(T i) {
return static_cast<Derived*>(this)->foo(i);
}
};
class B : public A<B, Y, X> {
public:
// Uncomment this line if you really want to overload foo
// instead of overriding. It's optional in this specific case.
//using A::foo;
unsigned foo(Y i) {
cout << "B" << endl;
return i.test_y();
}
};
I don't think the overloads in derived classes cannot be used from the base class where they are to be called from.
What you can do, is to extract the foo method, so you can specialize it for B.
#include <iostream>
using namespace std;
class X {
public:
unsigned test_x() {
return 1;
}
};
class Y {
public:
unsigned test_y() {
return 2;
}
};
template <typename T, typename U>
struct Foo
{
unsigned foo(U i) {
cout << "A" << endl;
return i.test_x();
}
};
template <typename T, typename U>
class A : public Foo<T, U> {
public:
unsigned bar(T i) {
return this->foo(i);
}
};
template <>
struct Foo<Y, X>
{
public:
unsigned foo(Y i) {
cout << "B" << endl;
return i.test_y();
}
};
class B : public A<Y, X> {
};
int main() {
B b = B();
Y y = Y();
cout << "Hello: " << b.bar(y) << endl;
return 0;
}
Edit: My first answer was incorrect.
When you instantiate A with classes Y and X the call of foo(T) generates an error because there no proper overloaded method defined in A. It suffices to declare a pure virtual method of foo(T) in A and implement this method in B.
template <typename T, typename U>
class A {
public:
virtual unsigned foo(T i) = 0;
unsigned foo(U i) { /* as above */ }
unsigned bar(T i) {
return foo(i); // calls abstract foo(T)
}
};
/* B is left untouched */
Compiling this generates this output:
B
Hello: 2