This question already has answers here:
Select class constructor using enable_if
(3 answers)
Closed 3 years ago.
I have two constructors that I would like to choose between based on a template parameter yes
template <bool yes>
class Base {
public:
template<typename std::enable_if< yes, int>::type = 0>
Base() { /* yes */ }
template<typename std::enable_if<!yes, int>::type = 0>
Base() { /* no */ }
};
I'm baffled why this produces the compiler error,
failed requirement '!true'; 'enable_if' cannot be used to disable this declaration
on Base<true> and
no type named 'type' in 'std::__1::enable_if<false, int>'; 'enable_if' cannot be used to disable this declaration
on Base<false>. None of the other variants that I can find including this and this and this work either. How can I select which constructor to use based on yes?
There are several issues here. The first thing is that the syntax for default template template paramers is wrong, it should be:
template <bool yes>
class Base {
public:
template<typename T=std::enable_if< yes, int>::type>
Base() { /* yes */ }
template<typename T=std::enable_if<!yes, int>::type>
Base() { /* no */ }
};
But that's not going to work either, because default parameter values are not a part of the template's signature, so, roughly speaking, this is equivalent to:
template<typename T>
Base() { /* yes */ }
template<typename T>
Base() { /* no */ }
That's how the both constructors' signatures look to the compiler. Both are templates with a single parameter, so for the purposes of overload resolutions both constructors have identical signatures, and that's not going to work any better than declaring two "Base(int foo)" constructors. You'll get the same error if you declare:
Base(int foo=0)
and
Base(int foo=1)
constructors. Two constructors, both have the same signature. Default values are not a part of a signature.
There are several traditional hacks to work around this. A common design pattern in the C++ library itself is to declare some helper empty classes and use them as additional parameters to disambiguate different methods for the purposes of overload resolution. For example, using std::in_place_t to select a particular overloaded constructor of std::optional, or std::in_place_type_t, for the equivalent functionality of std::variant's constructor.
In your case here, we can make use of the placeholder parameter completely automatic, in conjunction with delegated constructors:
#include <iostream>
struct bool_true {};
struct bool_false {};
template<bool value> class bool_value;
template<>
struct bool_value<true> {
typedef bool_true type;
};
template<>
struct bool_value<false> {
typedef bool_false type;
};
template<bool v>
using bool_value_t=typename bool_value<v>::type;
template <bool yes>
class Base {
public:
Base() : Base{ bool_value_t<yes>{} } {}
Base(const bool_true &)
{
std::cout << "Yes" << std::endl;
}
Base(const bool_false &)
{
std::cout << "No" << std::endl;
}
};
int main()
{
Base<true> t;
Base<false> f;
return 0;
}
Related
I have created an options struct, intended to be used via designated initializer:
struct FooOptions {
bool enableReticulation;
};
void Foo(FooOptions&& opts);
Foo(FooOptions{.enableReticulation = true});
Unfortunately, because bool has a default constructor, it's also valid to do this:
Foo(FooOptions{});
but I don't want this; I want users of Foo to explicitly decide to enable reticulation or not. I can achieve this with a runtime error this way:
struct FooOptions {
bool enableReticulation = []() -> bool {
assert(false, "No value provided for enableReticulation");
}();
};
But I would prefer to do this with a compile time error. Is there any way to do that? I am OK with changing bool to SomeWrapper<bool> if necessary, as long I can mostly initialize SomeWrapper<T> as if it were T, but without a default initializer.
You clarified that this is about arbitrary classes, and not primitive types. For arbitrary classes, with arbitrary constructors: just delete the constructor, but explicitly delete the default constructor:
template<typename T> class SomeWrapper : public T {
SomeWrapper()=delete;
using T::T;
};
Then:
#include <vector>
foo F{ {1,2} }; // Works, initializes the vector with {1,2}
foo G{}; // Fails
This may not work like you want for primitive types. Just specialize SomeWrapper as needed. There aren't that many primitive types to deal with.
Way to handle classes and non-classes types, thank to SFINAE:
template<typename T, typename Enabler = void> class TWrapper;
template<typename T>
class TWrapper<T, std::enable_if_t<std::is_class<T>::value>> : public T {
public:
TWrapper()=delete;
using T::T;
};
template<typename T>
class TWrapper<T, std::enable_if_t<!std::is_class<T>::value>>
{
public:
TWrapper()=delete;
T value;
TWrapper(T arg) : value(arg) {}
operator T() const { return value; }
};
Demo
#include <iostream>
using namespace std;
template<bool enable, typename T>
struct foo;
template<typename T>
struct foo<false , T>
{
//nothing
};
template<typename T>
struct foo<true , T>
{
void say_hello()
{
cout << "Hello !" << endl;
}
protected:
int m_some_data_when_I_enabled{};
};
template<bool Enable, typename T>
struct bar
:
foo<Enable , T>
{
//And there are lots of functions and members
//Here I need conditional 'using'
using foo<Enable , T>::say_hello;
void say_hello(int different_signature)
{
}
};
struct duck { };
int main(int, char**) {
bar<true , duck> enabled_bar;
bar<false , duck> disabled_bar;
}
It gives an error when I declare bar. It makes sense to me. So I need something like :
template<typename = typename std::enable_if<Enable>::type>
using foo<Enable , T>::say_hello();
I know I can solve the problem with specializing 'bar' but It has some members and in that case I would duplicate lots of codes. Is there a different, maybe tricky, way ?
You can declare a deleted say_hello in your 1st foo which makes the using statement in bar legal.
template<typename T>
struct foo<false , T>
{
void say_hello() = delete;
};
Here is the full example.
You can add a simple forwarding overload, and use SFINAE to conditionally disable it when the member does not exist in the base class. Like this:
template<typename V = T, typename = decltype(&foo<Enable, V>::say_hello)>
void say_hello()
{
bar::foo::say_hello();
}
See it Live
We need to use &foo<Enable, V>::say_hello instead of &foo<Enable, T>::say_hello to delay the check and make it happen during substitution (when an attempt is made to call the function) as opposed to it happening when bar is instantiated.
If the member doesn't exist, overload resolution will discard the new overload (on account of an ill-formed substitution), as though it never existed.
But it's worth noting that this won't work for overload sets (because one cannot take a pointer-to-member of an overload set). It will only work if you know there is only a single overload, which can have its pointer-to-member formed unambiguously.
What is the workaround to get this to compile?
#include <iostream>
template <typename Derived>
struct CRTP {
void foo (const typename Derived::type& a) {std::cout << a << '\n';}
};
struct A : CRTP<A> {
using type = int;
};
struct B : CRTP<B> {
using type = std::string;
};
// etc...
int main() {
A a;
a.foo(5);
}
This will not compile, because at the time of instantiation of CRTP<A>, A isn't a complete class yet, so A::type cannot be accessed. But what is the workaround? I need this type of design so that the foo function can be used generically for many different classes.
A somewhat crazier alternative is to defer evaluation until an attempt is made to call foo, by which point Derived would be complete. This requires making it a template.
template <typename Derived>
struct CRTP {
template<class T = Derived>
void foo (const typename T::type& a) {std::cout << a << '\n';}
};
It is trivial to block calling foo with a type that isn't Derived, if desired, via a static_assert.
I'm pretty sure you can't use the CRTP on a 'using' case. You can use it for methods and members, but not things like types. When using templates though, having types as template parameters is what it is so useful for, so why not do
template <typename Derived, typename Type>
....
Which will work perfectly fine.
I have a templatized class like so :
template<typename T>
class A
{
protected:
std::vector<T> myVector;
public:
/*
constructors + a bunch of member functions here
*/
}
I would like to add just ONE member function that would work only for 1 given type of T. Is it possible to do that at all without having to specialize the class and reimplement all the other already existing methods?
Thanks
The simplest and cleanest solution is to use a static_assert() in the body of a method, rejecting other types than the selected one (in the below example only integers are accepted):
#include <type_traits>
#include <vector>
template <typename T>
class A
{
public:
void onlyForInts(T t)
{
static_assert(std::is_same<T, int>::value, "Works only with ints!");
}
protected:
std::vector<T> myVector;
};
int main()
{
A<int> i;
i.onlyForInts(1); // works !
A<float> f;
//f.onlyForInts(3.14f); // does not compile !
}
OK CASE DEMO
NOK CASE DEMO
This utilizes the fact that a compiler instantiates a member function of a class template only when one is actually used (not when the class template is instantiated itself). And with the above solution, when a compiler tries to do so, it fails due to the execution of a static_assert.
C++ Standard Reference:
§ 14.7.1 Implicit instantiation [temp.inst]
Unless a function template specialization has been explicitly instantiated or explicitly specialized, the function template specialization is implicitly instantiated when the specialization is referenced in a context that requires a function definition to exist. Unless a call is to a function template explicit specialization or to a member function of an explicitly specialized class template, a default argument for a function template or a member function of a class template is implicitly instantiated when the function is called in a context that requires the value of the default argument.
[ Example:
template<class T> struct Z {
void f();
void g();
};
void h() {
Z<int> a; // instantiation of class Z<int> required
Z<char>* p; // instantiation of class Z<char> not required
Z<double>* q; // instantiation of class Z<double> not required
a.f(); // instantiation of Z<int>::f() required
p->g(); // instantiation of class Z<char> required, and
// instantiation of Z<char>::g() required
}
Nothing in this example requires class Z<double>, Z<int>::g(), or Z<char>::f() to be implicitly
instantiated. — end example ]
Yes, it's possible in C++03 with CRTP (Curiously recurring template pattern):
#include <numeric>
#include <vector>
template<typename Derived, typename T>
struct Base
{
};
template<typename Derived>
struct Base<Derived, int>
{
int Sum() const
{
return std::accumulate(static_cast<Derived const*>(this)->myVector.begin(), static_cast<Derived const*>(this)->myVector.end(), int());
}
};
template<typename T>
class A : public Base<A<T>, T>
{
friend class Base<A<T>, T>;
protected:
std::vector<T> myVector;
public:
/*
constructors + a bunch of member functions here
*/
};
int main()
{
A<int> Foo;
Foo.Sum();
}
As an alternative solution, which works also in plain C++03 (as opposed to static_assert or enable_if solutions), you may add extra defaulted template argument which will let you have both
specialized and unspecialized version of class. Then you can inherit your specialized version from the unspecialized one.
Here is a sample snippet:
#include <vector>
template<typename T, bool unspecialized = false>
class A
{
protected:
std::vector<T> myVector;
public:
void setVec(const std::vector<T>& vec) { myVector = vec; }
/*
constructors + a bunch of member functions here
*/
};
template<>
class A<int, false> : public A<int, true>
{
public:
int onlyForInt() {
return 25;
}
};
int main() {
// your code goes here
std::vector<int> vec;
A<int> a;
a.setVec(vec);
a.onlyForInt();
return 0;
}
The drawbacks of this solution is the need to add constructor forwarders, if class
has non-trivial constructors.
The static_assert technique by #PiotrS. works nicely. But it's also nice to know that you can specialize a single member function without code duplication. Just give the generic onlyForInts() an empty no-op implementation, and specialize it out-of-class for int
#include <vector>
template <typename T>
class A
{
public:
void onlyForInts(T t)
{
// no-op
}
protected:
std::vector<T> myVector;
};
template<>
void A<int>::onlyForInts(int t)
{
// works
}
int main()
{
A<int> i;
i.onlyForInts(1); // works !
A<float> f;
f.onlyForInts(3.14f); // compiles, but does nothing !
}
Live Example.
This technique comes in handy if you want to have int specific behavior without completely disabling the generic behavior.
One approach not given yet in the answers is using the standard library std::enable_if to perform SFINAE on a base class that you inherit to the main class that defines appropriate member functions.
Example code:
template<typename T, class Enable = void>
class A_base;
template<typename T>
class A_base<T, typename std::enable_if<std::is_integral<T>::value>::type>{
public:
void only_for_ints(){/* integer-based function */}
};
template<typename T>
class A_base<T, typename std::enable_if<!std::is_integral<T>::value>::type>{
public:
// maybe specialize for non-int
};
template<typename T>
class A: public A_base<T>{
protected:
std::vector<T> my_vector;
};
This approach would be better than an empty function because you are being more strict about your API and better than a static_cast because it simply won't make it to the inside of the function (it won't exist) and will give you a nice error message at compile time (GCC shows "has no member named ‘only_for_ints’" on my machine).
The downside to this method would be compile time and code bloat, but I don't think it's too hefty.
(don't you dare say that C++11 requirement is a down-side, we're in 2014 god-damnit and the next standard has even be finalized already!)
Also, I noticed, you will probably have to define my_vector in the base class instead of the final because you probably want to handle that data within the member function.
A nice way to do that without duplicating a bunch of code is to create a base base class (good god) and inherit that class in the base class.
Example:
template<typename T>
class base_data{
protected:
std::vector<T> my_vector;
};
template<typename T>
class A_base<T, typename std::enable_if<std::is_integral<T>::value>::type>: public base_bata<T>{
public:
void only_for_ints(){/* phew, finally. fiddle around with my_vector! */}
};
// non-integer A-base
template<typename T>
class A: public A_base<T>{
protected:
// helper functions not available in base
};
That does leave a horrible looking multiple-inheritance scheme, but it is very workable and makes it easy to define members based on template parameters (for future proofing).
People often don't like multiple-inheritance or how complicated/messy SFINAE looks, but I couldn't live without it now that I know of it: the speed of static code with the polymorphism of dynamic code!
Not sure where I found this, but you can use = delete; as the function definition inside the class, thereby deleting the function for the general case, and then explicitly specialize outside the class:
template <typename T>
struct A
{
auto int_only(T) -> void = delete;
};
template <> auto A<int>::int_only(int) -> void {}
int main()
{
auto a_int = A<int>{};
auto a_dbl = A<double>{};
a_int.int_only(0);
// a_dbl.int_only(3.14); error: call to deleted member function
}
https://en.cppreference.com/w/cpp/language/function#Deleted_functions
I would like to define a C++ template specialization that applies to all subclasses of a given base class. Is this possible?
In particular, I'd like to do this for STL's hash<>. hash<> is defined as an empty parametrized template, and a family of specializations for specific types:
template<class _Key>
struct hash { };
template<>
struct hash<char>
{
size_t
operator()(char __x) const
{ return __x; }
};
template<>
struct hash<int>
{
size_t
operator()(int __x) const
{ return __x; }
};
...
I would like to define something like this:
template<class Base>
struct hash {
size_t operator()(const Base& b) const {
return b.my_hash();
}
};
class Sub : public Base {
public:
size_t my_hash() const { ... }
};
and be able to use it like this:
hash_multiset<Sub> set_of_sub;
set_of_sub.insert(sub);
However, my hash template conflicts with the generic one from STL. Is there a way (perhaps using traits) to define a template specialization that applies to all subclasses of a given base class (without modifying the STL definitions)?
Note that I know I can do this with some extra template parameters whenever this hash specialization is needed, but I'd like to avoid this if possible:
template<>
struct hash<Base> {
size_t operator()(const Base& b) const {
return b.my_hash();
}
};
....
// similar specialization of equal_to is needed here... I'm glossing over that...
hash_multiset<Sub, hash<Base>, equal_to<Base> > set_of_sub;
set_of_sub.insert(sub);
Since C++ 11 you can use SFINAE together with standard library enable_if and is_base_of to solve the problem.
C++20 makes a cleaner solution possible - basically equivalent to enable_if, which even (optionally) works with CRTP
#include <concepts>
#include <functional>
#include <unordered_set> // just for demo in main()
template <class T>
class Base {};
class Derived final : public Base<Derived> {};
template<class T>
requires std::derived_from<T, Base<T>>
struct std::hash<T> {
// constexpr is optional
constexpr size_t operator() (const T& value) const noexcept {
return 0xDEADBEEF; // FIXME: do something better :)
}
};
int main() {
// If operator() weren't constexpr, this couldn't be a *static* assert
static_assert(std::hash<Derived>()(Derived {}) == 0xDEADBEEF);
std::unordered_set<Derived> usageTest;
return 0;
}
The solution is to use SFINAE to decide whether or not to allow your specialisation depending on the class inheritance structure. In Boost you can use enable_if and is_base_of to implement this.
http://www.boost.org/doc/libs/1_47_0/libs/utility/enable_if.html
http://www.boost.org/doc/libs/1_47_0/libs/type_traits/doc/html/boost_typetraits/reference/is_base_of.html
This was the best I could do:
template<>
struct hash<Sub> : hash<Base> {
};
I'm a little worried that I didn't have to make operator() virtual, though.
I don't think it is possible, because the way to do template specialization based on something more complex than just matching the type is C++ SFINAE, which requires second (dummy) template argument. Unfortunatelly, std::hash takes only one template argument and it is not allowed to create another version of std::hash with two template arguments. Therefore, the if you aren't satisfied with Jayen's solution, you can create your own hash type:
#include <iostream>
#include <type_traits>
using namespace std;
class ParentClass {};
class ChildClass : public ParentClass {};
// SFINAE, if T is not a child of ParentClass, substitution will fail
// You can create specializations for the general case, for another base classes, etc.
template<typename T, typename=typename enable_if<is_base_of<ParentClass, T>::value, T>::type>
struct your_hash
{
size_t operator()(const T& value)
{
return 42;
}
};
int main()
{
ParentClass pc;
ChildClass cc;
cout<<your_hash<ParentClass>()(pc)<<"\n";
cout<<your_hash<ChildClass>()(cc)<<"\n";
}