This is possible:
struct A {
//void f(); < not declared in struct A
};
template<typename T>
struct Wrapper {
T t;
void call_f() { t.f(); }
};
int main() {
Wrapper<A> w;
}
This compiled fine, as long as w.call_f() is not called. w.call_f() can not be instantiated because A::f does not exist.
I'm having a situation with such a wrapper template that gets used with different T types, which do not always implement all parts of the interface. (Mainly to avoid code duplication).
This does not work:
struct A {
//using i_type = int; < not declared/defined
};
template<typename T>
struct Wrapper {
using its_i_type = typename T::i_type;
// compile error, even if `i_type` gets never used
};
int main() {
Wrapper<A> w;
}
neither does this:
struct A {
//using i_type = int; < not declared/defined
};
template<typename T>
struct Wrapper {
typename T::i_type call_f() { return 0; }
// does not compile, even if `call_f()` is never instantiated
};
int main() {
Wrapper<A> w;
}
Is there a good way to handle these situations, without a lot of code duplication (like a specialization for Wrapper, etc.)?
You can defer the type deduction of its_i_type. Basically, you create a simple wrapper that you must go through.
To extend it to other types you require, (I wanted to suggest type_traits-like solution, but since you don't want specializations) you could define all the types you need:
template<typename T>
struct Wrapper {
private:
template<typename U> struct i_typper { using type = typename U::i_type; };
template<typename U> struct k_typper { using type = typename U::k_type; };
template<typename U> struct p_typper { using type = typename U::p_type; };
public:
using i_trait = i_typper<T>;
using k_trait = k_typper<T>;
using p_trait = p_typper<T>;
};
Example:
struct A { using i_type = int; };
struct B { using i_type = int; using k_type = float; };
int main() {
Wrapper<A> w; //Works now.
Wrapper<A>::i_trait::type mk1; //Works
Wrapper<A>::k_trait::type mk2; //Fails, not defined
Wrapper<B>::i_trait::type mk3; //Works
Wrapper<B>::k_trait::type mk4; //Works
}
For the case of:
template<typename T>
struct Wrapper {
typename T::i_type call_f() { return 0; }
// does not compile, even if `call_f()` is never instantiated
};
You have few options here:
make that function a member function template
Use some form of type_traits mechanism, which will still involve specialization
Go the way of abstracting common Wrapper stuff in a base class WrapperBase;
For the first option, you'll have to modify it a bit to further defer deduction
template<typename T>
struct Wrapper {
private:
template<typename U, typename> struct i_typper { using type = typename U::i_type; };
template<typename U, typename> struct k_typper { using type = typename U::k_type; };
template<typename U, typename> struct p_typper { using type = typename U::p_type; };
public:
using i_trait = i_typper<T, void>;
using k_trait = k_typper<T, void>;
using p_trait = p_typper<T, void>;
template<typename U = void>
typename k_typper<T, U>::type call_f() { return 0; }
};
I'll leave the second option as an exercise: (it may end up being something like:
template<typename T>
struct wrapper_traits {
....
};
template<>
struct wrapper_traits<A>{
using ....
};
template<typename T>
struct Wrapper {
....
public:
using i_trait = wrapper_traits<T>;
using k_trait = wrapper_traits<T>;
using p_trait = wrapper_traits<T>;
};
Jarod's answer is simpler. But this will work if you do not have access to std::experimental, or your company code policy forbids you...
With std::experimental::is_detected, you may do
template<typename T>
using i_type_t = typename T::i_type;
template<typename T>
struct Wrapper {
using its_i_type = typename std::experimental::detected_t<i_type_t, T>;
// would be T::i_type or std::experimental::nonesuch
};
Or to better handle case, something like:
template<typename T, bool = std::experimental::is_detected<i_type_t, T>::value>
struct WrapperWithIType {
// Empty for false case.
};
template<typename T>
struct WrapperWithIType<T, true> {
using its_i_type = i_type_t<T>;
its_i_type call_f() { return 0; }
};
and then
template<typename T>
struct Wrapper : WrapperWithIType<T> {
// Common stuff
};
Related
I have some CRTP dependency that I am not sure how to resolve. Ideally I want to put as many things as possible in the base class, like functions, so I do not have to redefine those for every class that inherits those. This seems to cause an issue with the initialization order, where result_type is dependent on the type that is yet to be initialized. Here is an example: https://godbolt.org/z/YpfcPB
And here is the code:
template<typename T>
struct CRTP_Derived;
template<typename Derived>
struct CRTP
{
using result_type = typename Derived::result_type;
};
template<typename T>
struct CRTP_Derived : public CRTP<CRTP_Derived<T>>
{
using result_type = T;
};
int main()
{
CRTP_Derived<int> a;
return 0;
}
I've also used a separate traits type for issues like this. You can reduce the needed boilerplate a little if you make the traits a second template parameter, instead of requiring users to specialize a separate template:
template<typename Derived, typename Traits>
struct CRTP
{
using result_type = typename Traits::result_type;
};
template<typename T>
struct CRTP_Derived_Traits
{
using result_type = T;
};
template<typename T>
struct CRTP_Derived : public CRTP<CRTP_Derived<T>, CRTP_Derived_Traits<T>>
{
};
int main()
{
CRTP_Derived<int> a;
return 0;
}
A workaround I found is taking out the typedef in a separate class, still I would be glad to see other solutions.
https://godbolt.org/z/a7NCE2
template<typename T>
struct CRTP_Derived;
template<typename Derived>
struct traits;
template<typename T>
struct traits<CRTP_Derived<T>>
{
using result_type = T;
};
template<typename Derived>
struct CRTP
{
using result_type = typename traits<Derived>::result_type;
};
template<typename T>
struct CRTP_Derived : public CRTP<CRTP_Derived<T>>
{
using result_type = T;
};
int main()
{
CRTP_Derived<int> a;
return 0;
}
I would like to know how to solve the following problem (C++17):
suppose there are several template classes, inherited from each other in CRTP-like fashion (single inheritance only). For a given instantiated template base class, find the class that is furthest from it down the inheritance chain.
I first thought that is should be pretty easy, but was not able to accomplish this.
To simplify, suppose that every root and every intermediate class has using DerivedT = Derived in its public area.
Example:
template <class T>
struct GetDeepest {
using Type = ...;
};
template <class T>
struct A {
using DerivedT = T;
};
template <class T>
struct B : public A<B<T>> {
using DerivedT = T;
};
struct C : B<C> {
};
struct D : A<D> {
};
GetDeepest<A<D>>::Type == D;
GetDeepest<B<C>>::Type == C;
GetDeepest<A<B<C>>>::Type == C;
...
First implementation I've tried:
template <class T>
struct GetDeepest {
template <class Test, class = typename Test::DerivedT>
static std::true_type Helper(const Test&);
static std::false_type Helper(...);
using HelperType = decltype(Helper(std::declval<T>()));
using Type = std::conditional_t<std::is_same_v<std::true_type, HelperType>,
GetDeepest<typename T::DerivedT>::Type,
T>;
};
Second implementation I've tried:
template <class T>
struct HasNext {
template <class Test, class = typename Test::DerivedT>
static std::true_type Helper(const Test&);
static std::false_type Helper(...);
using HelperType = decltype(Helper(std::declval<T>()));
static const bool value = std::is_same_v<std::true_type, HelperType>;
};
template <class T>
auto GetDeepestHelper(const T& val) {
if constexpr(HasNext<T>::value) {
return GetDeepestHelper(std::declval<typename T::DerivedT>());
} else {
return val;
}
}
template <class T>
struct GetDeepest {
using Type = decltype(GetDeepestLevelHelper(std::declval<T>()));
};
None of them compile.
First one -- because of incomplete type of GetDeepest<T> in statement using Type = ..., second because of recursive call of a function with auto as a return type.
Is it even possible to implement GetDeepest<T> class with such properties? Now I'm very curious, even if it might be not the best way to accomplish what I want.
Thanks!
I'm not sure if I fully understand the question so feel free to ask me in comments.
But I think this should work:
#include <type_traits>
template<typename T>
struct GetDeepest
{
using Type = T;
};
template<template<typename> class DT, typename T>
struct GetDeepest<DT<T>>
{
using Type = typename GetDeepest<T>::Type;
};
template <class T>
struct A {
using DerivedT = T;
};
template <class T>
struct B : public A<B<T>> {
using DerivedT = T;
};
struct C : B<C> {
};
struct D : A<D> {
};
int main()
{
static_assert(std::is_same<GetDeepest<A<D>>::Type, D>::value);
static_assert(std::is_same<GetDeepest<B<C>>::Type, C>::value);
static_assert(std::is_same<GetDeepest<A<B<C>>>::Type, C>::value);
}
I would like to have access to a nested class using templates, and can't figure out how to do it: A sample code:
template <typename T> class mything {
typedef unsigned key;
T data;
public:
template <typename subT> class mysubthing { typedef subT value_type; };
using subthing = mysubthing<T>;
using subthing_ro = mysubthing<const T>;
};
template<typename T> struct X; // a container for all value_types
#ifdef MAKE_IT_FAIL
// this should automatically set the X<mything<T>::mysubthing<subT>
template<typename T,typename subT> struct X<typename mything<T>::template mysubthing<subT>> {
using value_type = subT;
};
#endif
typedef mything<int> intthing;
#ifndef MAKE_IT_FAIL
template<> struct X<mything<int>::subthing> { using value_type = int; };
template<> struct X<mything<int>::subthing_ro> { using value_type = const int; };
#endif
int main(void) {
intthing t;
X<intthing::subthing>::value_type data = 1; // a data object
X<intthing::subthing_ro>::value_type data_ro = 1; // read-only data object
return 0;
}
This compiles without -DMAKE_IT_FAIL, but of course it completely misses the point about templates, since what I wanted was entered manually. How can I make it work with -DMAKE_IT_FAIL?
You can't specialize like that:
template<typename T,typename subT>
struct X<typename mything<T>::template mysubthing<subT>> {
since deduction of T from types like outer<T>::anything_after is impossible (not supported) in C++.
You don't really need specialization in this general case at all. Just define the default X, and then only specialize the other cases:
template <typename T> class mything {
typedef unsigned key;
T data;
public:
template <typename subT> struct mysubthing
{
typedef subT value_type;
};
using subthing = mysubthing<T>;
using subthing_ro = mysubthing<const T>;
};
template<typename T> struct X
{
using value_type = typename T::value_type;
};
// this should automatically set the X<mything<T>::mysubthing<subT>
typedef mything<int> intthing;
template<> struct X<mything<int>::subthing> { using value_type = int; };
template<> struct X<mything<int>::subthing_ro> { using value_type = const int; };
int main(void) {
intthing t;
X<intthing::subthing>::value_type data = 1; // a data object
X<intthing::subthing_ro>::value_type data_ro = 1; // read-only data object
return 0;
}
Addendum
According to one of the comments, X is actually std::iterator_traits, which is already defined. In that case, the only way around it is to define the iterator class outside the mything class:
template <typename T, typename subT>
class mything_iterator {
typedef subT value_type;
};
template <typename T> class mything {
typedef unsigned key;
T data;
public:
using iterator = mything_iterator<T, T>;
using const_iterator = mything_iterator<T, const T>;
};
namespace std {
template<typename T, class subT>
class iterator_traits<mything_iterator<T, subT>>{
using value_type =typename mything_iterator<T, subT>::value_type;
// etc...
};
template<> struct iterator_traits<mything<int>::iterator>
{ using value_type = int; };
template<> struct iterator_traits<mything<int>::const_iterator>
{ using value_type = int; };
}
What I want to do is to create a generic traits class with the default logic and then write code to specialize each specific case with only the things that are different from the generic case. My goal is to remove code duplication and avoid writing unnecessary code.
I'll give you an example:
int genericFunction(); // defined somewhere else
int specialFunction(); // defined somewhere else
template<int id>
struct IdTraits
{
using MyType = int;
using AnotherType = double;
static constexpr auto&& f = genericFunction;
};
template<>
struct IdTraits<1>
{
// Using MyType and AnotherType of IdTraits generic case [how?]
static constexpr auto&& f = specialFunction;
};
template<>
struct IdTraits<2>
{
// Using MyType and f of IdTraits generic case [how?]
using AnotherType = char;
};
template<int id, class Traits = IdTraits<id>>
struct General
{
void foo(int arg)
{
Traits::MyType myType;
Traits::AnotherType anotherType;
Traits::f(arg);
// Do stuff with myType and anotherType
}
};
Do you think is theoretically possible to do something like this?
You can have a second Trait to use instead that will do this work. Its purpose will be to check the presence of each element inside the current IdTrait<id> and set the default type/function if not.
Using current experimental detection for the two types, and member getF for the function:
template<int id>
struct MyIdTraits {
template <typename T> using MyType_t = typename T::MyType;
using MyType = std::experimental::detected_or_t<int, MyType_t, IdTraits<id>>;
template <typename T> using AnotherType_t = typename T::AnotherType;
using AnotherType = std::experimental::detected_or_t<double, AnotherType_t, IdTraits<id>>;
template <typename T, typename = decltype(T::f)>
static constexpr auto getF(int) { return T::f; }
template <typename T>
static constexpr auto getF(unsigned) { return genericFunction; }
static constexpr auto&& f = getF<IdTraits<id>>(42);
};
Then replace your trait by this one:
template<int id, class Traits = MyIdTraits<id>>
struct General { ... };
Demo
Yes. Put your generic case in a base class:
namespace details
{
struct IdTraits_generic
{
using MyType = int;
using AnotherType = double;
static constexpr auto&& f = genericFunction;
};
}
template<int id> struct IdTraits : details::IdTraits_generic
{
};
template<> struct IdTraits<1> : details::IdTraits_generic
{
static constexpr auto&& f = specialFunction;
};
template<> struct IdTraits<2> : details::IdTraits_generic
{
using AnotherType = char;
};
What I need:
template <class Context/*if Context has a Buffer typedef*/>
struct Buffer {
typedef typename Context::Buffer type;
};
template <class Context/*if Context doesn't have a Buffer typedef*/>
struct Buffer {
typedef std::shared_ptr<void> type;
};
If the parameter class Context has a Buffer typedef, then it's used, otherwise shared_ptr would be used.
How to write the templates? thanks in advance.
This is fairly straightforward using a void_t helper:
#include <memory>
#include <type_traits>
template <typename...> using void_t = void;
template <class Context,class = void>
struct Buffer {
typedef std::shared_ptr<void> type;
};
template <class Context>
struct Buffer<Context,void_t<typename Context::Buffer> > {
typedef typename Context::Buffer type;
};
int main()
{
struct Context1 {
};
struct Context2 {
typedef int Buffer;
};
{
using A = Buffer<Context1>::type;
using B = std::shared_ptr<void>;
static_assert(std::is_same<A,B>::value,"");
}
{
using A = Buffer<Context2>::type;
using B = int;
static_assert(std::is_same<A,B>::value,"");
}
}
Note that std::void_t is in C++17, but it is easy enough to create your own.
Use partial class template specializations with SFINAE. It works even with C++11.
(Live example)
struct A
{
using tag = void;
static constexpr const char *name = "A";
};
struct B
{
static constexpr const char *name = "B";
};
template <typename T> struct S
{
static void func()
{
std::cout << T::name << " - no tag\n";
};
};
template <typename T, typename = typename T::tag> using enable_if_has_tag = T;
template <typename T> struct S<enable_if_has_tag<T>>
{
static void func()
{
std::cout << T::name << " - has tag\n";
};
};
int main()
{
S<A>::func(); // -> `A - has tag`
S<B>::func(); // -> `B - no tag`
}