I have several functions that I would like to work for derived classes of a CRTP base class. The issue is that if I pass the derived classes into the free functions meant for the CRTP class, ambiguities arise. A minimal example to illustrate this is this code:
template<typename T>
struct A{};
struct C : public A<C>{};
struct B{};
template<typename T, typename U>
void fn(const A<T>& a, const A<U>& b)
{
std::cout << "LT, RT\n";
}
template<typename T, typename U>
void fn(const T a, const A<U>& b)
{
std::cout << "L, RT\n";
}
template<typename T, typename U>
void fn(const A<T>& a, const U& b)
{
std::cout << "LT, R\n";
}
int main()
{
C a; // if we change C to A<C> everything works fine
B b;
fn(a,a); // fails to compile due to ambiguous call
fn(b,a);
fn(a,b);
return 0;
}
Ideally I would like this to work for the derived classes as it would if I were to use the base class (without having to redefine everything for the base classes, the whole point of the CRTP idiom was to not have to define fn for multiple classes).
First, you need a trait to see if something is A-like. You cannot just use is_base_of here since you don't know which A will be inherited from. We need to use an extra indirection:
template <typename T>
auto is_A_impl(A<T> const&) -> std::true_type;
auto is_A_impl(...) -> std::false_type;
template <typename T>
using is_A = decltype(is_A_impl(std::declval<T>()));
Now, we can use this trait to write our three overloads: both A, only left A, and only right A:
#define REQUIRES(...) std::enable_if_t<(__VA_ARGS__), int> = 0
// both A
template <typename T, typename U, REQUIRES(is_A<T>() && is_A<U>())
void fn(T const&, U const&);
// left A
template <typename T, typename U, REQUIRES(is_A<T>() && !is_A<U>())
void fn(T const&, U const&);
// right A
template <typename T, typename U, REQUIRES(!is_A<T>() && is_A<U>())
void fn(T const&, U const&);
Note that I'm just taking T and U here, we don't necessarily want to downcast and lose information.
One of the nice things about concepts coming up in C++20 is how much easier it is to write this. Both the trait, which now becomes a concept:
template <typename T> void is_A_impl(A<T> const&);
template <typename T>
concept ALike = requires(T const& t) { is_A_impl(t); }
And the three overloads:
// both A
template <ALike T, ALike U>
void fn(T const&, U const&);
// left A
template <ALike T, typename U>
void fn(T const&, U const&);
// right A
template <typename T, ALike U>
void fn(T const&, U const&);
The language rules already enforce that the "both A" overload is preferred when it's viable. Good stuff.
Given that in your example the first element of the second function and the second element of the third should not inherit from the CRTP you can try something like the following:
#include<iostream>
#include<type_traits>
template<typename T>
struct A{};
struct C : public A<C>{};
struct B{};
template<typename T, typename U>
void fn(const A<T>& a, const A<U>& b)
{
std::cout << "LT, RT\n";
}
template<typename U>
struct isNotCrtp{
static constexpr bool value = !std::is_base_of<A<U>, U>::value;
};
template<typename T, typename U, std::enable_if_t<isNotCrtp<T>::value, int> = 0>
void fn(const T a, const A<U>& b)
{
std::cout << "L, RT\n";
}
template<typename T, typename U, std::enable_if_t<isNotCrtp<U>::value, int> = 0>
void fn(const A<T>& a, const U& b)
{
std::cout << "LT, R\n";
}
int main()
{
C a;
B b;
fn(a,a);
fn(b,a);
fn(a,b);
return 0;
}
Basically we disable the second and third functions when passing a CRTP in first and second argument, leaving only the first function available.
Edit: answering to OP comment, if T and U both inherit the first will be called, wasn't this the expected behavior?
Play with the code at: https://godbolt.org/z/ZA8hZz
Edit: For a more general answer, please refer to the one posted by user Barry
This is one of those situations when it's convenient to create a helper class that can be partially specialized to do this, with the function turned into a wrapper that selects the appropriate specialization:
#include <iostream>
template<typename T>
struct A{};
struct C : public A<C>{};
struct B{};
template<typename T, typename U>
struct fn_helper {
static void fn(const T &a, const U &b)
{
std::cout << "L, R\n";
}
};
template<typename T, typename U>
struct fn_helper<T, A<U>> {
static void fn(const T &a, const A<U> &b)
{
std::cout << "L, RT\n";
}
};
template<typename T, typename U>
struct fn_helper<A<T>, U> {
static void fn(const A<T> &a, const U &b)
{
std::cout << "LT, R\n";
}
};
template<typename T, typename U>
struct fn_helper<A<T>, A<U>> {
static void fn(const A<T> &a, const A<U> &b)
{
std::cout << "LT, RT\n";
}
};
template<typename T, typename U>
void fn(const T &a, const U &b)
{
fn_helper<T,U>::fn(a, b);
}
int main()
{
A<C> a;
B b;
fn(a,a);
fn(b,a);
fn(a,b);
return 0;
}
Output (gcc 9):
LT, RT
L, RT
LT, R
I would expect modern C++ compilers to require selecting only their most modest optimization level to completely optimize away the wrapping function call.
Related
The below code fails to compile. For some reason inheriting from HasFoo causes IsWrapper to fail. It has something to do with the friend function foo() because inheriting from other classes seems to work fine. I don't understand why inheriting from HasFoo causes the detection idiom to fail.
What is the proper way to detect WithFoo as a Wrapper?
https://godbolt.org/z/VPyarN
#include <type_traits>
#include <iostream>
template<typename TagType, typename ValueType>
struct Wrapper {
ValueType V;
};
// Define some useful metafunctions.
template<typename Tag, typename T>
T BaseTypeImpl(const Wrapper<Tag, T> &);
template<typename T>
using BaseType = decltype(BaseTypeImpl(std::declval<T>()));
template<typename Tag, typename T>
Tag TagTypeImpl(const Wrapper<Tag, T> &);
template<typename T>
using TagType = decltype(TagTypeImpl(std::declval<T>()));
// Define VoidT. Not needed with C++17.
template<typename... Args>
using VoidT = void;
// Define IsDetected.
template<template <typename...> class Trait, class Enabler, typename... Args>
struct IsDetectedImpl
: std::false_type {};
template<template <typename...> class Trait, typename... Args>
struct IsDetectedImpl<Trait, VoidT<Trait<Args...>>, Args...>
: std::true_type {};
template<template<typename...> class Trait, typename... Args>
using IsDetected = typename IsDetectedImpl<Trait, void, Args...>::type;
// Define IsWrapper true if the type derives from Wrapper.
template<typename T>
using IsWrapperImpl =
std::is_base_of<Wrapper<TagType<T>, BaseType<T>>, T>;
template<typename T>
using IsWrapper = IsDetected<IsWrapperImpl, T>;
// A mixin.
template<typename T>
struct HasFoo {
template<typename V,
typename std::enable_if<IsWrapper<T>::value &&
IsWrapper<V>::value>::type * = nullptr>
friend void foo(const T &This, const V &Other) {
std::cout << typeid(This).name() << " and " << typeid(Other).name()
<< " are wrappers\n";
}
};
template<typename Tag>
struct WithFoo : public Wrapper<WithFoo<Tag>, int>,
public HasFoo<WithFoo<Tag>> {};
int main(void) {
struct Tag {};
WithFoo<Tag> WrapperFooV;
// Fails. Why?
static_assert(IsWrapper<decltype(WrapperFooV)>::value,
"Not a wrapper");
return 0;
}
I don't understand why inheriting from HasFoo causes the detection idiom to fail.
Isn't completely clear to me also but surely a problem is that you use IsWrapper<T> inside the body of HasFoo<T> and, when you inherit HasFoo<WithFoo<Tag>> from WithFoo<Tag> you have that WithFoo<Tag> is incomplete when you check it with IsWrapper.
A possible solution (I don't know if acceptable for you) is define (and SFINAE enable/disable) foo() outside HasFoo.
I mean... try rewriting HasFoo as follows
template <typename T>
struct HasFoo {
template <typename V>
friend void foo(const T &This, const V &Other);
};
and defining foo() outside
template <typename T, typename V>
std::enable_if_t<IsWrapper<T>::value && IsWrapper<V>::value>
foo(const T &This, const V &Other) {
std::cout << typeid(This).name() << " and " << typeid(Other).name()
<< " are wrappers\n";
}
What is the proper way to detect WithFoo as a Wrapper?
Sorry but your code is too complicated for me.
I propose the following (simpler, I hope) alternative
#include <type_traits>
#include <iostream>
template<typename TagType, typename ValueType>
struct Wrapper {
ValueType V;
};
template <typename T1, typename T2>
constexpr std::true_type IW_helper1 (Wrapper<T1, T2> const &);
template <typename T>
constexpr auto IW_helper2 (T t, int) -> decltype( IW_helper1(t) );
template <typename T>
constexpr std::false_type IW_helper2 (T, long);
template <typename T>
using IsWrapper = decltype(IW_helper2(std::declval<T>(), 0));
template <typename T>
struct HasFoo {
template <typename V>
friend void foo(const T &This, const V &Other);
};
template <typename T, typename V>
std::enable_if_t<IsWrapper<T>::value && IsWrapper<V>::value>
foo(const T &This, const V &Other) {
std::cout << typeid(This).name() << " and " << typeid(Other).name()
<< " are wrappers\n";
}
template<typename Tag>
struct WithFoo : public Wrapper<WithFoo<Tag>, int>,
public HasFoo<WithFoo<Tag>> {};
int main () {
struct Tag {};
WithFoo<Tag> WrapperFooV;
static_assert(IsWrapper<decltype(WrapperFooV)>::value,
"Not a wrapper");
}
I have a class
template <typename T, typename W>
class A {
void foo(W);
void foo(T);
void foo(int);
}
When T=int, W=int, or W=T, this class fails to compile. How can I get the methods to take priority over each other?
I want the priority W > T > int. So if W=T, foo(T) is ignored and foo(W) is called. If T=int, foo(int) is ignored and foo(T) is called.
The compiler is VS2012, but I have Linux too, and will consider GCC/Clang solutions as well. Anything that compiles on any mainstream compiler goes, but only if you say what compilers it works on.
I would tag dispatch. Override dispatching is easy to understand and scales.
We start with a perfect forwarder:
template<class U> void foo(U&&u){
foo( std::forward<U>(u), std::is_convertible<U, W>{}, std::is_convertible<U,T>{} );
}
it creates tag types, in this case true or false types, to dispatch on.
This one:
void foo( W, std::true_type, ... );
catches everything that can convert to W.
Next, we block this one:
void foo( T, std::false_type, std::true_type );
from considerimg cases where the first argument can convert to W.
Finally, this one:
void foo( int, std::false_type, std::false_type );
can only be considered if the first parameter cannot convert to either.
Fancier tag types, or doing the dispatching one at a time, are both possible.
Sorry for typos.
I use a single C++11 feature -- {} to construct an object -- above. If your compiler lacks support for that C++11 feature, simply upgrade your compiler, it is 2014, get with it. Failing that, replace {} with ().
Use std::enable_if:
#include <type_traits>
template <typename T, typename W>
struct A {
void foo(W) {}
template<typename XT=T> typename std::enable_if<std::is_same<XT,T>::value
&& !std::is_same<T, W>::value, void>::type foo(T) {}
template<typename XT=int> typename std::enable_if<std::is_same<XT,int>::value
&& !std::is_same<int, T>::value
&& !std::is_same<int, W>::value, void>::type foo(int) {}
};
Added for testing:
template struct A<short,char>;
template struct A<char,char>;
template struct A<char,int>;
template struct A<int,char>;
template struct A<int, int>;
struct S {};
int main() {
A<S, int>{}.foo(S{});
}
For the relevant part of your template, you could use speclializations:
template <typename U, typename W>
struct Foo
{
void f(U);
void f(W);
};
template <typename T>
struct Foo<T, T>
{
void f(T);
};
For the rest of your class or class template, you can inherit from Foo<A, B> so you can keep the common code out of the part that needs to be specialized:
template <typename A, typename B>
struct TheClass : Foo<A, B>
{
// common code
};
Try template specializations:
template <typename T, typename W>
class A {
void foo(W);
void foo(T);
void foo(int);
};
template <typename T>
class A<T, T> {
void foo(T);
void foo(int);
};
template <>
class A<int, int> {
void foo(int);
};
Here is a solution without specializations of A, but with two helper structures in a few forms.
#include <iostream>
template<typename T, typename W>
struct T_type { typedef T type; };
template<typename W>
struct T_type<W, W> { typedef void* type; /*dummy type*/};
template<typename T, typename W>
struct int_type { typedef int type; };
template<typename W>
struct int_type<int, W> { typedef void** type; /*dummy type*/};
template<typename T>
struct int_type<T, int> { typedef void** type; /*dummy type*/};
template<>
struct int_type<int, int> { typedef void** type; /*dummy type*/};
template<typename T, typename W>
class A {
public:
void foo(W w) {
std::cout << "foo(W)" << std::endl;
}
void foo(typename T_type<T, W>::type t) {
std::cout << "foo(T)" << std::endl;
}
void foo(typename int_type<T, W>::type i) {
std::cout << "foo(int)" << std::endl;
}
};
int main() {
std::cout << "A<float, char>" << std::endl;
A<float, char> a;
a.foo(1.0f);
a.foo('1');
a.foo(1);
std::cout << "A<float, float>" << std::endl;
A<float, float> b;
b.foo(1.0f);
b.foo(1);
std::cout << "A<int, int>" << std::endl;
A<int, int> c;
c.foo(1);
return 0;
}
I'm trying to implement is_polymorphic_functor meta-function to get the following results:
//non-polymorphic functor
template<typename T> struct X { void operator()(T); };
//polymorphic functor
struct Y { template<typename T> void operator()(T); };
std::cout << is_polymorphic_functor<X<int>>::value << std::endl; //false
std::cout << is_polymorphic_functor<Y>::value << std::endl; //true
Well that is just an example. Ideally, it should work for any number of parameters, i.e operator()(T...). Here are few more test cases which I used to test #Andrei Tita's solution which fails for two test-cases.
And I tried this:
template<typename F>
struct is_polymorphic_functor
{
private:
typedef struct { char x[1]; } yes;
typedef struct { char x[10]; } no;
static yes check(...);
template<typename T >
static no check(T*, char (*) [sizeof(functor_traits<T>)] = 0 );
public:
static const bool value = sizeof(check(static_cast<F*>(0))) == sizeof(yes);
};
which attempts to make use of the following implementation of functor_traits:
//functor traits
template <typename T>
struct functor_traits : functor_traits<decltype(&T::operator())>{};
template <typename C, typename R, typename... A>
struct functor_traits<R(C::*)(A...) const> : functor_traits<R(C::*)(A...)>{};
template <typename C, typename R, typename... A>
struct functor_traits<R(C::*)(A...)>
{
static const size_t arity = sizeof...(A) };
typedef R result_type;
template <size_t i>
struct arg
{
typedef typename std::tuple_element<i, std::tuple<A...>>::type type;
};
};
which gives the following error for polymorphic functors:
error: decltype cannot resolve address of overloaded function
How to fix this issue and make is_polymorphic_functor work as expected?
This works for me:
template<typename T>
struct is_polymorphic_functor
{
private:
//test if type U has operator()(V)
template<typename U, typename V>
static auto ftest(U *u, V* v) -> decltype((*u)(*v), char(0));
static std::array<char, 2> ftest(...);
struct private_type { };
public:
static const bool value = sizeof(ftest((T*)nullptr, (private_type*)nullptr)) == 1;
};
Given that the nonpolymorphic functors don't have an overloaded operator():
template<typename T>
class is_polymorphic_functor {
template <typename F, typename = decltype(&F::operator())>
static constexpr bool get(int) { return false; }
template <typename>
static constexpr bool get(...) { return true; }
public:
static constexpr bool value = get<T>(0);
};
template<template<typename>class arbitrary>
struct pathological {
template<typename T>
typename std::enable_if< arbitrary<T>::value >::type operator(T) const {}
};
The above functor is non-polymorphic iff there is exactly one T such that arbitrary<T>::value is true.
It isn't hard to create an template<T> functor which is true on int and possibly double, and only true on double if (arbitrary computation returns 1).
So an uncompromising is_polymorphic is beyond the scope of this universe.
If you don't like the above (because it clearly takes more than just int, other types simply fail to find an overload), we could do this:
template<template<typename>class arbitrary>
struct pathological2 {
void operator()(int) const {}
template<typename T>
typename std::enable_if< arbitrary<T>::value >::type operator(T) const {}
};
where the second "overload" is tested, and if there are no T such that it is taken, then the first overload occurs for every single type.
This question follows this one : Function overloading and template deduction priority
Considering the following classes :
template<typename T1, typename T2>
class Base {};
class Derived0 : public Base<double, double> {};
template<typename T1, typename T2, typename T3>
class Derived1 : public Base<T1, T2> {};
template<typename T1, typename T2, typename T3, typename T4>
class Derived2 : public Base<T3, T4> {};
And the following functions :
template<typename T> f(const T& x); // version A
template<typename T1, typename T2> f(const Base<T1, T2>& x); // version B
My problem is that f(double) will call version A (ok), f(Base<double, double>) will call version B (ok), but f(Derived1<double, double, double>) will call version A (see the link to the other question at the beginning).
Using C++11, how to block version A and force version B for all derived members of Base<T1, T2> whatever T1 and T2 are ?
Note : If possible, I would like to avoid to add helper classes and prefer adding members to the provided classes.
Here's a trait that might work for you.
The trait class:
#include <type_traits>
template <typename, typename> struct Base { };
template <typename T> struct isbase
{
typedef char yes;
typedef yes no[2];
template <typename U, typename V> static yes & test(Base<U, V> const &);
static no & test(...);
static bool const value = sizeof(test(std::declval<T>())) == sizeof(yes);
};
Application:
#include <iostream>
template <typename T>
typename std::enable_if<!isbase<T>::value>::type f(T const &)
{
std::cout << "f(T const &)\n";
}
template <typename T1, typename T2>
void f(Base<T1, T2> const &)
{
std::cout << "f(Base<T1, T2> const &)\n";
}
Example:
template<typename T1, typename T2, typename T3>
struct Derived1 : public Base<T1, T2> {};
int main()
{
std::cout << isbase<double>::value << std::endl;
std::cout << isbase<Base<int, char>>::value << std::endl;
std::cout << isbase<Derived1<bool, bool, bool>>::value << std::endl;
f(double{});
f(Base<int, char>{});
f(Derived1<bool, float, long>{});
}
Generalization: We can make a more general trait to check if a type derives from a template instance:
template <typename T, template <typename...> class Tmpl>
struct derives_from_template
{
typedef char yes;
typedef yes no[2];
template <typename ...Args> static yes & test(Tmpl<Args...> const &);
static no & test(...);
static bool const value = sizeof(test(std::declval<T>())) == sizeof(yes);
};
Usage: derives_from_template<T, Base>::value etc.
I think you can give you B class template a marker type as member and cause instantiation of the general tempkate to fail when present. Normally, SFINAE works the other way around but using an indirection it should still work.
template<typename T1, typename T2>
class Base {
public:
struct isBase {};
};
template <typename T>
struct is_base {
template <typename S> char (&test(typename S::isBase*))[1];
template <typename S> char (&test(...))[2];
enum { value = sizeof(test<T>(0)) == 1 };
};
template <typename T>
typename std::enable_if<!is_base<T>::value>::type f(T value) {
...
};
The solution basically does something similar to KerrekSB's solution but doesn't require explicit spelling out the supported types: It uses the tag isBasd (which should probably be spelled more unique) to detect objects of type Base or derived.
I got class with template methods that looks at this:
struct undefined {};
template<typename T> struct is_undefined : mpl::false_ {};
template<> struct is_undefined<undefined> : mpl::true_ {};
template<class C>
struct foo {
template<class F, class V>
typename boost::disable_if<is_undefined<C> >::type
apply(const F &f, const V &variables) {
}
template<class F, class V>
typename boost::enable_if<is_undefined<C> >::type
apply(const F &f, const V &variables) {
}
};
apparently, both templates are instantiated, resulting in compile time error.
is instantiation of template methods different from instantiation of free functions?
I have fixed this differently, but I would like to know what is up.
the only thing I can think of that might cause this behavior, enabling condition does not depend immediate template arguments, but rather class template arguments
Thank you
Your C does not participate in deduction for apply. See this answer for a deeper explanation of why your code fails.
You can resolve it like this:
template<class C>
struct foo {
template<class F, class V>
void apply(const F &f, const V &variables) {
apply<F, V, C>(f, variables);
}
private:
template<class F, class V, class C1>
typename boost::disable_if<is_undefined<C1> >::type
apply(const F &f, const V &variables) {
}
template<class F, class V, class C1>
typename boost::enable_if<is_undefined<C1> >::type
apply(const F &f, const V &variables) {
}
};