C++ detection idiom failure with inheritance - c++

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");
}

Related

Check if a type is the same as a templated type

I have a user defined class
template<typename T, int N>
class MyClass
{
// Implementation
};
and I want to check on the instantiation of another class if its template parameter is an instance of MyClass
template<typename T, std::enable_if_t<!is_MyClass<T>, bool> = true>
class MapClass
{
// custom stuff here
};
template<typename T, std::enable_if_t<is_MyClass<T>, bool> = true>
class MapClass
{
// Some more stuff here
};
I tried to implement it like this but my instantiation fails because it requires two parameters. How do I make automatically extract both parameters
template <typename T> struct is_MyClass : std::false_type {};
template <typename T, int N> struct is_MyClass<MyClass<T, N>> : std::true_type {};
Thanks
I suggest to write a trait is_instantiation_of_myClass that uses partial specialization:
template<typename T, int N>
class MyClass {};
template <typename C>
struct is_instantiation_of_myClass : std::false_type {};
template <typename T,int N>
struct is_instantiation_of_myClass<MyClass<T,N>> : std::true_type {};
template <typename C>
constexpr bool is_instantiation_of_myClass_v = is_instantiation_of_myClass<C>::value;
Now you can do SFINAE based on is_instantiation_of_myClass<T> or just plain specialization:
template <typename T,bool = is_instantiation_of_myClass_v<T>>
struct Foo;
template <typename T>
struct Foo<T,true> {
static constexpr bool value = true;
};
template <typename T>
struct Foo<T,false> {
static constexpr bool value = false;
};
int main() {
std::cout << Foo< int >::value << "\n";
std::cout << Foo< MyClass<int,42>>::value << "\n";
}
Live Demo

Multiple template functions with enable_if and is_same and with missing argument list of the class template in C++

I have the following code which compiles nicely:
#include <iostream>
struct Res {};
struct Jac {};
template <typename T, typename S>
class A;
template <typename S>
class A<Res, S>
{
public:
A() { std::cout << "A<Res, S>" << std::endl; }
};
template <typename S>
class A<Jac, S>
{
public:
A() { std::cout << "A<Jac, S>" << std::endl; }
};
template <typename T, typename S>
class B;
template <typename S>
class B<Res, S>
{
public:
B() { std::cout << "B<Res, S>" << std::endl; }
};
template <typename S>
class B<Jac, S>
{
public:
B() { std::cout << "B<Jac, S>" << std::endl; }
};
template<typename S, typename EvalT,
std::enable_if_t<std::is_same<EvalT, A<Res,S>>::value, bool> = true
>
void foo()
{
A<Res, S> a_res;
A<Jac, S> a_jac;
}
template<typename S, typename EvalT,
std::enable_if_t<std::is_same<EvalT, B<Res,S>>::value, bool> = true
>
void foo()
{
B<Res, S> b_res;
B<Jac, S> b_jac;
}
int main() {
foo<int, A<Res,int>>();
foo<int, B<Res,int>>();
return 0;
}
However I am not happy with the calls inside my main() function. I would like them to look like this:
foo<int, A>();
foo<int, B>();
which would imply the following modification of the templates for foo():
template<typename S, typename EvalT,
std::enable_if_t<std::is_same<EvalT, B>::value, bool> = true
>
void foo()
{
B<Res, S> b_res;
B<Jac, S> b_jac;
}
This obviously does not compile. The idea is to have a function, which would instantiate either A or B without explicitly specifying T for my classes because I know that foo() has to create 2 instances with Res and Jac as type parameters. Is there any way to make the code neater and achieve such a behavior?
You can change foo to accept a template template parameter CT that is templated on two types, and enable_if the specific overload based on whether CT<Res, S> is the same type as A<Res, S>, or B<Res, S>:
template<typename S, template<typename, typename> typename CT,
std::enable_if_t<std::is_same<CT<Res,S>, A<Res,S>>::value, bool> = true
>
void foo()
{
A<Res, S> a_res;
A<Jac, S> a_jac;
}
template<typename S, template<typename, typename> typename CT,
std::enable_if_t<std::is_same<CT<Res, S>, B<Res,S>>::value, bool> = true
>
void foo()
{
B<Res, S> b_res;
B<Jac, S> b_jac;
}
Here's a demo.

why C++ template type matching does not match to base class refrence, how can I make it match to base class refrence?

Here is my C++ program code
#include <iostream>
#include <memory>
using namespace std;
template <typename T>
struct A { T x; };
template <typename T>
struct B:A<T> { };
template <typename T>
void func(const A<T>& t) {
cout<<"2"<<endl;
}
template <typename T>
void func(const T& t) {
cout<<"1"<<endl;
}
int main() {
B<int> b;
func(b);
}
and it prints 1. But what I expect is the function call prints 2. Why B<int>b matches to const T& instead of const A<T>&. And how can I make it to match to const A<T>&?
Solution 1
You could use std::enable_if and templated template parameters to get a better match for the function, like:
#include <iostream>
#include <memory>
#include <type_traits>
using namespace std;
template <typename T>
struct A { T x; };
template <typename T>
struct B:A<T> { };
template <template <typename...> typename T, typename ...Args, typename = std::enable_if_t<std::is_base_of_v<A<Args...>, T<Args...>>>>
void func(const T<Args...>& t) {
cout<<"2"<<endl;
}
template <typename T>
void func(const T& t) {
cout<<"1"<<endl;
}
int main() {
B<int> b;
func(b);
func(5);
}
However, this works only if A takes exactly the same template parameters as T. So if your B changes to ex.
template <typename T, typename U>
struct B : A<T> {}
this won't work anymore.
Solution 2
Based on Yakk's answer you can create a type trait that is more generic. This solution has no restrictions to its template parameters, like solution 1 does.
namespace detail
{
template <template <typename...> typename Base>
struct template_base_detector
{
template <typename... Args>
constexpr std::true_type operator()(Base<Args...>*);
constexpr std::false_type operator()(...);
};
}
template <template <typename...> typename Base, typename T>
struct is_template_base_of
: decltype(std::declval<detail::template_base_detector<Base>>()((T*)nullptr)) {};
// since C++ 14
template <template <typename...> typename Base, typename T>
constexpr bool is_template_base_of_v = is_template_base_of<Base, T>::value;
Depending on your c++ version, you can use different approaches to utilize this trait.
C++ 17
Probably the most compact solution. Since C++ 17 constexpr if statements are allowed, which allows us to define just a single func:
template <typename T>
void func(const T& t)
{
if constexpr (is_template_base_of_v<A, T>)
cout << 2 << endl;
else
cout << 1 << endl;
}
C++ 11 and 14
We have to fall back to tag dispatch:
namespace detail
{
template <typename T>
void func(std::true_type, const T& t)
{
std::cout << 2 << endl;
}
template <typename T>
void func(std::false_type, const T& t)
{
std::cout << 1 << endl;
}
}
template <typename T>
void func(const T& t)
{
detail::func(is_template_base_of<A, T>{}, t);
}
Tag dispatching.
First, we write a trait to detect if something has a template as a base:
namespace details {
template<template<class...>class Z>
struct htb {
template<class...Ts>
constexpr std::true_type operator()(Z<Ts...>*){return {};}
constexpr std::false_type operator()(...){return {};}
};
}
template<template<class...>class Z, class X>
constexpr inline auto has_template_base = details::htb<Z>{}((X*)nullptr);
we can now use our new trait to tag dispatch:
namespace details{
template <typename T>
void func(std::true_type,const A<T>& t) {
std::cout<<"2"<<std::endl;
}
template <class T>
void func(std::false_type,const T& t) {
std::cout<<"1"<<std::endl;
}
}
template <typename T>
void func(const T& t) {
details::func(has_template_base<A,T>,t);
}
Live example.

How to define type trait for containers with index operator?

I have the following type traits to distinguish between fundamental and container types:
template <typename T>
using enable_if_fundamental_t = std::enable_if_t<std::is_fundamental_v<T>>;
template <typename T, typename = void>
struct is_container : std::false_type {};
template <typename T>
struct is_container<
T
, std::void_t<
typename T::value_type
, typename T::size_type
, typename T::allocator_type
, typename T::iterator
, typename T::const_iterator
, decltype(std::declval<T>().size())
, decltype(std::declval<T>().begin())
, decltype(std::declval<T>().end())
, decltype(std::declval<T>().cbegin())
, decltype(std::declval<T>().cend())
>
> : std::true_type {};
template <typename T>
constexpr bool is_container_v = is_container<T>::value;
template <typename T>
using enable_if_container_t = std::enable_if_t<is_container_v<T>>;
and they are used with the following functions:
template <typename T, typename = enable_if_fundamental_t<T>>
void foo(T)
{
std::cout << "This is a fundamental type" << std::endl;
}
template <typename C, typename = enable_if_container_t<C>>
void foo(const C& c)
{
std::cout << "This is a container type" << std::endl;
}
with the following arguments:
std::list<std::uint32_t> l;
std::vector<std::uint32_t> v;
std::map<std::string, std::uint32_t> m;
std::unordered_map<std::string, std::uint32_t> um;
std::uint32_t i = 42;
foo(l);
foo(v);
foo(m);
foo(um);
foo(i);
and they work fine.
Now I want to distinguish containers which has overloaded operator[] from the others. I tried the following code:
template <typename T, typename = void>
struct is_container_with_index_operator_with_size_type : std::false_type {};
template <typename T>
struct is_container_with_index_operator_with_size_type<
T
, std::void_t<
enable_if_container_t<T>
, decltype(std::declval<T>().operator[](std::declval<typename T::size_type>()))
>
> : std::true_type {};
template <typename T>
constexpr bool is_container_with_index_operator_with_size_type_v =
is_container_with_index_operator_with_size_type<T>::value;
template <typename T, typename = void>
struct is_container_with_index_operator_with_key_type : std::false_type {};
template <typename T>
struct is_container_with_index_operator_with_key_type<
T
, std::void_t<
enable_if_container_t<T>
, typename T::key_type
, decltype(std::declval<T>().operator[](std::declval<typename T::key_type>()))
>
> : std::true_type {};
template <typename T>
constexpr bool is_container_with_index_operator_with_key_type_v =
is_container_with_index_operator_with_key_type<T>::value;
template <typename T>
constexpr bool is_container_with_index_operator_v =
is_container_with_index_operator_with_size_type_v<T> ||
is_container_with_index_operator_with_key_type_v<T>;
template <typename T>
constexpr bool is_container_without_index_operator_v =
is_container_v<T> &&
!is_container_with_index_operator_v<T>;
template <class T>
using enable_if_container_with_index_operator_t =
std::enable_if_t<is_container_with_index_operator_v<T>>;
template <class T>
using enable_if_container_without_index_operator_t =
std::enable_if_t<is_container_without_index_operator_v<T>>;
with the following overloads:
template <typename T, typename = enable_if_fundamental_t<T>>
void foo(T)
{
std::cout << "This is a fundamental type" << std::endl;
}
template <typename C, typename = enable_if_container_without_index_operator_t<C>>
void foo(const C&)
{
std::cout << "This is a container type without index operator" << std::endl;
}
template <typename C, typename = enable_if_container_with_index_operator_t<C>>
void foo(const C&)
{
std::cout << "This is a container type with index operator" << std::endl;
}
with the same arguments as above, bit it produced me the error:
error C2995: 'void foo(const C &)': function template has already been defined
I tried several variations of the code above, but I didn't manage to do it the right way.
How to do this the right way and is it possible to achieve a simpler code? For example without separate meta functions for containers which use size_type and key_type for the operator[]?
I'm using Visual Studio 2017 version 15.7.2 with v141 toolset and enabled /std:c++17.
That's because you can't overload functions based on default template parameter values alone. You can reproduce that with:
template <typename T, typename = std::enable_if_t<(sizeof(T) > 2)>> void foo() {}
template <typename T, typename = std::enable_if_t<!(sizeof(T) > 2)>> void foo() {}
// error: redefinition of 'template<class T, class> void foo()'
A possible solution is to use enable_if_t in a type of a template parameter:
template <typename T, std::enable_if_t<(sizeof(T) > 2), int> = 0> void foo() {}
template <typename T, std::enable_if_t<!(sizeof(T) > 2), int> = 0> void foo() {}
Or in return type:
template <typename T> std::enable_if_t<(sizeof(T) > 2)/*,void*/> foo() {}
template <typename T> std::enable_if_t<!(sizeof(T) > 2)/*,void*/> foo() {}

c++03: Mutually exclusive methods thanks to enable_if

Within a class, I have two different methods which should be mutually exclusive depending on the caller template parameter.
class Foo
{
// For collections
template<class T>
typename boost::enable_if<boost::is_same<typename std::vector<typename T::value_type>, T>::value, const T&>::type
doSomething()
{ }
// For single types
template<class T>
typename boost::enable_if<!boost::is_same<typename std::vector<typename T::value_type>, T>::value, const T&>::type
doSomething()
{ }
}
This won't compile.
error: type/value mismatch at argument 1 in template parameter list for 'template struct boost::enable_if'
error: expected a type, got '! boost::is_same::value'
How about:
template <typename T> struct is_std_vector : std::false_type {};
template <typename T, typename A>
struct is_std_vector<std::vector<T, A>> : std::true_type {};
And then
class Foo
{
// For collections
template<class T>
typename std::enable_if<is_std_vector<T>::value, const T&>::type
doSomething();
// For single types
template<class T>
typename std::enable_if<!is_std_vector<T>::value, const T&>::type
doSomething();
};
Unlike std's version, boost::enable_if accepts a type (kinda wrapper under boolean value), so you should write something like
class Foo
{
// For collections
template<class T>
typename boost::enable_if<
typename boost::is_same<typename std::vector<typename T::value_type>, T>,
const T&>::type doSomething()
{ }
// For single types
template<class T>
typename boost::enable_if_с<
!boost::is_same<typename std::vector<typename T::value_type>, T>::value,
const T&>::type doSomething()
{ }
}
Note here, I've used typename before boost::is_same and haven't used ::value in the first specification. On the contrary, I had to use enable_if_с in the second overload, because ! operator isn't applicable to a type.
What about a sort of tag dispatching?
#include <vector>
#include <iostream>
template <typename, typename>
struct isSame
{ typedef int type; };
template <typename T>
struct isSame<T, T>
{ typedef long type; };
struct foo
{
template <typename T>
T const & doSomething (T const & t, int)
{ std::cout << "int version" << std::endl; return t; }
template <typename T>
T const & doSomething (T const & t, long)
{ std::cout << "long version" << std::endl; return t; }
template <typename T>
T const & doSomething (T const & t)
{ return doSomething(t, typename isSame<
typename std::vector<typename T::value_type>, T>::type()); }
};
int main ()
{
foo f;
std::vector<int> v;
f.doSomething(v); // print "long version"
}
If what you want is to overload the function based on whether you are given a vector or not
#include <type_traits>
#include <iostream>
#include <vector>
using std::cout;
using std::endl;
class Foo {
public:
// For collections
template <class T>
const vector<T>& do_something(const std::vector<T>& input) {
cout << __PRETTY_FUNCTION__ << endl;
return input;
}
// For single types
template <class T>
const T& do_something(const T& input) {
cout << __PRETTY_FUNCTION__ << endl;
return input;
}
};
int main() {
auto foo = Foo{};
auto v = std::vector<int>{};
auto i = int{};
foo.do_something(v);
foo.do_something(i);
}
If you want to be even more general and check for any instantiated type
#include <type_traits>
#include <iostream>
#include <vector>
using std::cout;
using std::endl;
namespace {
template <typename T, template <typename...> class TT>
struct IsInstantiationOf
: public std::integral_constant<bool, false> {};
template <template <typename...> class TT, typename... Args>
struct IsInstantiationOf<TT<Args...>, TT>
: public std::integral_constant<bool, true> {};
} // namespace anonymous
class Foo {
public:
// For collections
template <typename VectorType, typename std::enable_if_t<IsInstantiationOf<
std::decay_t<VectorType>, std::vector>::value>* = nullptr>
void do_something(VectorType&&) {
cout << "Vector overload" << endl;
}
// For single types
template <class T, typename std::enable_if_t<!IsInstantiationOf<
std::decay_t<T>, std::vector>::value>* = nullptr>
void do_something(T&&) {
cout << "Non vector overload" << endl;
}
};
int main() {
auto foo = Foo{};
auto v = std::vector<int>{};
auto i = int{};
foo.do_something(v);
foo.do_something(i);
}
Also please note that you should avoid putting std::enable_if in the function signature as much as possible for these reasons https://stackoverflow.com/a/14623831/5501675