Note: everything that follows uses the Concepts TS implementation in GCC 6.1
Let's say I have a concept Surface, like the following:
template <typename T>
concept bool Surface() {
return requires(T& t, point2f p, float radius) {
{ t.move_to(p) };
{ t.line_to(p) };
{ t.arc(p, radius) };
// etc...
};
}
Now I want to define another concept, Drawable, which matches any type with a member function:
template <typename S>
requires Surface<S>()
void draw(S& surface) const;
i.e.
struct triangle {
void draw(Surface& surface) const;
};
static_assert(Drawable<triangle>(), ""); // Should pass
That is, a Drawable is something which has a templated const member function draw() taking an lvalue reference to something which satisfies the Surface requirements. This is reasonably easy to specify in words, but I can't quite work out how to do it in C++ with the Concepts TS. The "obvious" syntax doesn't work:
template <typename T>
concept bool Drawable() {
return requires(const T& t, Surface& surface) {
{ t.draw(surface) } -> void;
};
}
error: 'auto' parameter not permitted in this context
Adding a second template parameter allows the concept definition to compile, but:
template <typename T, Surface S>
concept bool Drawable() {
return requires(const T& t, S& s) {
{ t.draw(s) };
};
}
static_assert(Drawable<triangle>(), "");
template argument deduction/substitution failed:
couldn't deduce template parameter 'S'
now we can only check whether a particular <Drawable, Surface> pair matches the Drawable concept, which isn't quite right. (A type D either has the required member function or it does not: that doesn't depend on which particular Surface we check.)
I'm sure it's possible to do what I'm after, but I can't work out the syntax and there aren't too many examples online yet. Does anybody know how to write a concept definition which requires type to have a constrained template member function?
What you're looking for is for a way for the compiler to synthesize an archetype of Surface. That is, some private, anonymous type that minimally satisfies the Surface concept. As minimally as possible. Concepts TS doesn't currently allow for a mechanism for automatically synthesizing archetypes, so we're left with doing it manually. It's quite a complicated process, since it's very easy to come up with archetype candidates that have way more functionality that the concept specifies.
In this case, we can come up with something like:
namespace archetypes {
// don't use this in real code!
struct SurfaceModel {
// none of the special members
SurfaceModel() = delete;
SurfaceModel(SurfaceModel const& ) = delete;
SurfaceModel(SurfaceModel&& ) = delete;
~SurfaceModel() = delete;
void operator=(SurfaceModel const& ) = delete;
void operator=(SurfaceModel&& ) = delete;
// here's the actual concept
void move_to(point2f );
void line_to(point2f );
void arc(point2f, float);
// etc.
};
static_assert(Surface<SurfaceModel>());
}
And then:
template <typename T>
concept bool Drawable() {
return requires(const T& t, archetypes::SurfaceModel& surface) {
{ t.draw(surface) } -> void;
};
}
These are valid concepts, that probably work. Note that there's a lot of room for even more refinement on the SurfaceModel archetype. I have a specific function void move_to(point2f ), but the concept just requires that it's callable with an lvalue of type point2f. There's no requirement that move_to() and line_to() both take an argument of type point2f, they could both take complete different things:
struct SurfaceModel {
// ...
struct X { X(point2f ); };
struct Y { Y(point2f ); };
void move_to(X );
void line_to(Y );
// ...
};
This kind of paranoia makes for a better archetype, and serves to illustrate how complex this problem could be.
Related
Suppose I have a deserialize function:
template <typename T>
T deserialize(const std::string& src);
// Specialized for various types
// ...
and want to define a Deserializable concept for types for which deserialize is specialized. Are there any difference between the following versions?
template <typename T>
concept Deserializable = requires(std::string s) { deserialize<T>(s); };
// concept Deserializable = requires(std::string& s) { deserialize<T>(s); };
// concept Deserializable = requires(const std::string s) { deserialize<T>(s); };
// concept Deserializable = requires(const std::string& s) { deserialize<T>(s); };
If requires was similar to a normal function, then I'd expect the 1st (resp. 3rd) version to be identical to the 2nd (resp. 4th) version, as the name s inside a normal function body is an lvalue. However, I've seen the standard library use reference types as requires parameters, for example here:
template< class T >
concept range = requires(T& t) {
ranges::begin(t); // equality-preserving for forward iterators
ranges::end (t);
};
which seems to suggest that reference vs. value matters.
More generally, do CV-qualifiers and references for requires parameters matter?
UPDATE: I came up with a test to show that requires indeed seems to behave like normal functions:
template <typename T>
void f(T&&, T&&) {}
template <typename T>
concept Foo = requires(int x, int& y, int&& z) {
f(x, y);
f(x, z);
f(x, 123); // won't compile: deduced int& and int
};
int main()
{
}
So it seems the requires(T& t) in the standard library example above is no different than requires(T t). Is this always the case?
If T is an rvalue reference type U&&, T& is U&, but that matters only if decltype or something like std::forward is used. The only other formal difference is that it prevents the parameter from being adjusted to be a pointer if it’s an array or function type, which also rarely matters.
It may also be meant to avoid suggesting that a type is copyable/movable (as is often required for by-value function parameters).
I want to test out the new concepts feature in c++20 and I was wondering if I can create a concept that checks for the existence of a function that is declared const.
I want the check to fail if the function exists with the right type but isn't const. I couldn't find anything relevant here: https://en.cppreference.com/w/cpp/concepts
I have this
template <typename T>
concept hasToString = requires (T val) {
{ val.toString() } /* const here gives error */ -> std::same_as<std::string>;
};
void f(hasToString auto bar)
{
std::cout << bar.toString();
}
You can make the parameter const:
template <typename T>
concept hasToString = requires (T const val) {
{ val.toString() } -> std::same_as<std::string>;
};
Concepts check usage patterns, and so if what you want to check is calling a member function on a const object, you need to construct that scenario.
Note that this depends on what you want to happen if T happens to be a reference type. If you want this to work:
void f(hasToString auto&& bar)
Then T might be a reference type, and if you still want it really be const, then you need to turn a type like T& into T const. The long-way of writing that is:
template <typename T>
concept hasToString = requires (std::remove_reference_t<T> const val) {
{ val.toString() } -> std::same_as<std::string>;
};
But if you do this enough times, you could consider adding an alias template to handle that.
You can always check the expression is well-formed when applied to a const object argument.
template <typename T>
concept hasToString = requires (T val) {
{ std::as_const(val).toString() } -> std::same_as<std::string>;
};
or
template <typename T>
concept hasToString = requires (T val, T const cval) {
{ cval.toString() } -> std::same_as<std::string>;
};
Adding an extra object parameter is probably more inline with how abstract concept requirements are defined (well, if your requires-expression checks for more than one requirement). You can have as many parameters as you want in the require-expression's parameter list.
I have use case similar to this question
I want to check what type of instance variable is stored in parameter without throwing an exception
class ParameterBase
{
public:
virtual ~ParameterBase() {}
template<class T> const T& get() const; //to be implimented after Parameter
template<class T, class U> void setValue(const U& rhs); //to be implimented after Parameter
};
template <typename T>
class Parameter : public ParameterBase
{
public:
Parameter(const T& rhs) :value(rhs) {}
const T& get() const {return value;}
void setValue(const T& rhs) {value=rhs;}
private:
T value;
};
//Here's the trick: dynamic_cast rather than virtual
template<class T> const T& ParameterBase::get() const
{ return dynamic_cast<const Parameter<T>&>(*this).get(); }
template<class T, class U> void ParameterBase::setValue(const U& rhs)
{ return dynamic_cast<Parameter<T>&>(*this).setValue(rhs); }
class Diagram
{
public:
ParameterBase* v;
int type;
};
What I want to be able to do is something like this
if (diagram.getParameter().type == int) {
}
How can I change this implementation so it will allow me to peek what type of Parameter is holding
Thanks for the answers , few more points
I am on C++ 11 so cannot use variant or any
Is there standard way of doing this. All I want is an instance variable of class that can be of multiple types (bounded) and while reading it check what type it is
The Simple Fix
The simple solution to your problem is to add a template function is<T>() to your ParameterBase that is defined in terms of dynamic_cast on a pointer. dynamic_cast with pointers return nullptr on failure, unlike references which will throw a std::bad_cast. For example:
class ParameterBase
{
public:
...
template <typename T>
bool is() const;
};
...
template <typename T>
bool ParameterBase::is() const
{
return dynamic_cast<const Parameter<T>*>(this) != nullptr;
}
The use would be simply:
if (diagram.getParameter().is<int>()) {
...
}
Note, however, that this whole design is not particularly nice. It has a cyclic dependency between the base and derived in a way that is highly coupled. Additionally it requires ParameterBase to exist as a pointer in order to operate correctly; where value-semantics would be much more coherent (if possible)
It would be better if you can use type-erasure, even if you define Parameter in terms of it (this is what C++17's std::any will do for you). The second answer in your linked question already describes what this may look like.
Type-erased Solution (c++11)
This uses C++11 features like forwarding references, rvalue-references, and unique_ptr -- but the concept can also be applied to earlier C++ versions.
For type-erasure, you would need an interface that encompasses at least these 2 features:
getting a reference to the templated type, and
getting an identifier for the current type.
Since interfaces in C++ can't be virtual, we have to get creative about returning the reference. C++ has void* which can be any kind of pointer. This can be bad if misused (such as casting between the wrong type); but if we know the underlying type, can be perfect. Thankfully here, we know the underlying type.
A quick form of type-erasure could be achieved with the following:
#include <type_traits> // std::decay
#include <utility> // std::forward
#include <typeinfo> // std::type_info, std::bad_cast
#include <memory> // std::unique_ptr
class Parameter
{
private:
// This is the interface we will implement in all instances
struct Interface {
virtual ~Interface() = default;
virtual void* get() = 0;
virtual const std::type_info& type() const = 0;
};
// This is the concrete instantiation of the above interfaces
template <typename T>
struct Concrete : public Interface {
template <typename U>
Concrete(U&& u) : m_value{std::forward<U>(u)} {}
void* get() { return &m_value; }
const std::type_info& type() const { return typeid(T); }
T m_value; // actually holds the value here
};
// This holds onto the interface, and only the interface
std::unique_ptr<Interface> m_interface;
public:
// Constructs a parameter and sets the first interface value
template <typename T>
explicit Parameter(T&& value)
: m_interface{new Concrete<typename std::decay<T>::type>{std::forward<T>(value)}}
{}
Parameter(Parameter&&) = default;
Parameter& operator=(Parameter&&) = default;
// Check if we are the same type by comparing the typeid
template <typename T>
bool is() const {
return typeid(T) == m_interface->type();
}
// Get the underlying value. Always check that we are the correct type first!
template <typename T>
const T& get() const {
// do the error handling ourselves
if (!is<T>()) { throw std::bad_cast{}; }
// cast void* to the underlying T*. We know this is safe
// because of our check above first
return (*static_cast<T*>(m_interface->get()));
}
// Set the underlying value. Always check that we are the correct type first!
template <typename T, typename U>
void set(U&& value) {
// do the error handling ourselves
if (!is<T>()) { throw std::bad_cast{}; }
(*static_cast<T*>(m_interface->get())) = std::forward<U>(value);
}
};
In the above, we take on the burden of detecting the underlying type ourselves -- but we remove the cyclic coupling. We now also have a proper value-type that we can move around like a normal variable, which is really helpful since it allows us to return this object from APIs without worrying about lifetime or ownership.
If copyability is desired as well, the interface can be extended to have a clone() function or something to return copies
Using this object, the code becomes:
if (parameter.is<int>()) {
/* treat parameter as an int */
}
Here's a small working example.
Type-erased Solution (c++17)
If you're looking for a finite set of instantiations, std::variant may be used for this purpose. If the number of possibly underlying types is unbounded, you should look into std::any
In either case, the use of a hierarchy here is superficial (at least in the current example) since the entire type-erasure can be reduced to a singular type with the ability to query the containment. This could be done easily, using std::any as an example:
#include <any> // std::any, std::any_cast
class Parameter
{
public:
// This implementation changes the active type if 'T' is not the same as the stored
// value. If you want to restrict this, you can do error checking here instead.
template <typename T>
void set(const T& value) { m_value = value; }
template <typename T>
const T& get() { return std::any_cast<const T&>(m_value); }
template <typename T>
bool is() const noexcept { return m_value.type() == typeid(T); }
private:
std::any m_value;
};
If you don't want the active member to change, this could be restricted by checking is<T>() first and handling the error somehow.
Querying the active type can be achieved simply by doing:
if (parameter.is<int>()) {
/* treat parameter as an int */
}
If the types are fixed, you can always use std::variant instead using std::has_alternative for the definition of is
Looks like you know in advance all possible types for the parameter (I'm saying that because you have a type field that is expected to be used as an enumeration). If that is the case, you may use the std::variant idiom:
class Diagram
{
public:
std::variant<Parameter<int>, Parameter<std::string>> v;
};
In this case you may use this code to get known the actual type:
switch(v.index()) {
case 0:
// int is used
case 1:
// string is used
}
For sure there are other alternatives. For example, if you have something of a type and you need to test if that is the type expect, you my use std::is_same template:
template <typename T>
class Parameter : public ParameterBase
{
public:
bool isOfTypeInt() const {
return std::is_same_v<T, int>;
}
private:
T value;
};
I am writing a class designed to shoot random 3D vectors, but I use several geometric libraries in my projects (one included in the 3D simulation, one included in the analysis framework, one which is not included in a more-than-1-GB framework...). Each of these libraries has its own vector definition, with different names for the same method, such has getX(), GetX(), Get(0)... to get the first Cartesian coordinate. But sometimes a common naming convention has been adopted and some method names are the same across two or more libraries.
Of course I want to use this code for any of these vectors, so I implemented a template class. The problem is the following: how do I adapt my code to all these method names, without specializing my class for each implementation (some share the same method names) ?
I managed to write a class using a method or another, now I would like to generalize to any number of method. Something which says: "If you have method 1, use this implementation, if you have method 2, use this other one,... and if you have none, then compilation error".
Currently the class looks like (reduced to the part shooting a random direction):
// First some templates to test the presence of some methods
namespace detail_rand {
// test if a class contains the "setRThetaPhi" method
template<class T>
static auto test_setRThetaPhi(int) ->
decltype(void(std::declval<T>().setRThetaPhi(0.,0.,0.)),
std::true_type{});
template<class T>
static auto test_setRThetaPhi(float)->std::false_type;
}
// true_type if the class contains the "setRThetaPhi" method
template<class T>
struct has_setRThetaPhi : decltype(detail_rand::test_setRThetaPhi<T>(0)) {};
// The actual class
template<class vector>
class Random
{
// everything is static for easy use, might change later
private:
Random() = delete;
Random(Random&) = delete;
// the distribution, random generator and its seed
static decltype(std::chrono::high_resolution_clock::now().time_since_epoch().count()) theSeed;
static std::default_random_engine theGenerator;
static std::uniform_real_distribution<double> uniform_real_distro;
// Shoot a direction, the actual implementation is at the end of the file
private: // the different implementations
static const vector Dir_impl(std::true_type const &);
static const vector Dir_impl(std::false_type const &);
public: // the wrapper around the implementations
inline static const vector Direction() {
return Dir_impl(has_setRThetaPhi<vector>());
}
};
/// initialisation of members (static but template so in header)
// the seed is not of cryptographic quality but here it's not relevant
template<class vector>
decltype(std::chrono::high_resolution_clock::now().time_since_epoch().count())
Random<vector>::theSeed =
std::chrono::high_resolution_clock::now().time_since_epoch().count();
template<class vector>
std::default_random_engine Random<vector>::theGenerator(theSeed);
template<class vector>
std::uniform_real_distribution<double> Random<vector>::uniform_real_distro(0.,1.);
/// Implementation of method depending on the actual type of vector
// Here I use the "setRThetaPhi" method
template<class vector>
const vector Random<vector>::Dir_impl(std::true_type const &)
{
vector v;
v.setRThetaPhi(1.,
std::acos(1.-2.*uniform_real_distro(theGenerator)),
TwoPi()*uniform_real_distro(theGenerator));
return std::move(v);
}
// Here I use as a default the "SetMagThetaPhi" method
// but I would like to test before if I really have this method,
// and define a default implementation ending in a compilation error
// (through static_assert probably)
template<class vector>
const vector Random<vector>::Dir_impl(std::false_type const &)
{
vector v;
v.SetMagThetaPhi(1.,
std::acos(1.-2.*uniform_real_distro(theGenerator)),
TwoPi()*uniform_real_distro(theGenerator));
return std::move(v);
}
Something which says: "If you have method 1, use this implementation, if you have method 2, use this other one,... and if you have none, then compilation error".
I wrote an article that explains how to implement exactly what you need in C++11, C++14 and C++17: "checking expression validity in-place with C++17".
I will synthesize the C++11 and C++14 solutions below - you can use them to normalize all the interfaces you're dealing with by wrapping them inside a single "common" one. You can then implement your algorithms on the "common" interface.
Assume that you have:
struct Cat { void meow() const; };
struct Dog { void bark() const; };
And you want to create a function template make_noise(const T& x) that calls x.meow() if valid, otherwise x.bark() if valid, otherwise produces a compiler error.
In C++11, you can use enable_if and the detection idiom.
You will need to create a type trait for every member you wish to check the existence of. Example:
template <typename, typename = void>
struct has_meow : std::false_type { };
template <typename T>
struct has_meow<T, void_t<decltype(std::declval<T>().meow())>>
: std::true_type { };
Here's an usage example using enable_if and trailing return types - this technique makes use of expression SFINAE.
template <typename T>
auto make_noise(const T& x)
-> typename std::enable_if<has_meow<T>{}>::type
{
x.meow();
}
template <typename T>
auto make_noise(const T& x)
-> typename std::enable_if<has_bark<T>{}>::type
{
x.bark();
}
In C++14, you can use generic lambdas and an implementation of static_if (here's a talk I gave at CppCon 2016 about a possible one) to perform the check with an imperative-like syntax.
You need a few utilities:
// Type trait that checks if a particular function object can be
// called with a particular set of arguments.
template <typename, typename = void>
struct is_callable : std::false_type { };
template <typename TF, class... Ts>
struct is_callable<TF(Ts...),
void_t<decltype(std::declval<TF>()(std::declval<Ts>()...))>>
: std::true_type { };
// Wrapper around `is_callable`.
template <typename TF>
struct validity_checker
{
template <typename... Ts>
constexpr auto operator()(Ts&&...) const
{
return is_callable<TF(Ts...)>{};
}
};
// Creates `validity_checker` by deducing `TF`.
template <typename TF>
constexpr auto is_valid(TF)
{
return validity_checker<TF>{};
}
After that, you can perform all of your checks inside a single overload of make_noise:
template <typename T>
auto make_noise(const T& x)
{
auto has_meow = is_valid([](auto&& x) -> decltype(x.meow()){ });
auto has_bark = is_valid([](auto&& x) -> decltype(x.bark()){ });
static_if(has_meow(x))
.then([&x](auto)
{
x.meow();
})
.else_if(has_bark(x))
.then([&x](auto)
{
x.bark();
})
.else_([](auto)
{
// Produce a compiler-error.
struct cannot_meow_or_bark;
cannot_meow_or_bark{};
})(dummy{});
}
Some macro black magic and if constexpr allow you to write this in C++17:
template <typename T>
auto make_noise(const T& x)
{
if constexpr(IS_VALID(T)(_0.meow()))
{
x.meow();
}
else if constexpr(IS_VALID(T)(_0.bark()))
{
x.bark();
}
else
{
struct cannot_meow_or_bark;
cannot_meow_or_bark{};
}
}
You could solve this by introducing your own names for the operations. Do this by creating a trait class and specialising it for each of the libraries. Something like this:
template <class Vector>
struct VectorTraits;
template <>
struct VectorTraits<Lib1::Vector>
{
static auto getX(const Lib1::Vector &v) { return v.GetX(); }
// ... etc.
};
template <>
struct VectorTraits<Lib2::Vector>
{
static auto getX(const Lib2::Vector &v) { return v.Get(0); }
// ... etc.
};
//Usage:
template <class vector>
auto norm2(const vector &v)
{
using V = VectorTraits<vector>;
return V::getX(v) * V::getX(v) + V::getY(v) + V::getY(v);
}
If you want static assertions for the unsupported operations, you can put them into the unspecialised template:
template <class T>
struct False : std::false_type {};
template <class Vector>
struct VectorTraits
{
static void getX(const Vector &)
{
static_assert(False<Vector>::value, "This type does not support getting x");
}
};
I'm trying to use C++11, rather than using C++ as C++98 (I come from C) and I've hit type-traits, now rather than jumping in to the standard I thought I'd try and solve the problem.
Usually I'd use inheritance to add methods based on the type, and rely on the user, but I want to use traits, now I don't expect the end user to use my "custom ones", this is because it's an experiment.
I started by creating a True and a False type, as so:
struct True {
static const bool value = true;
};
struct False {
static const bool value = false;
};
Then my enable_if definition, which I understand to use the fact that a struct or class is both a struct (in the C sense, has a size and such) and a namespace, as so:
template<bool B,class T>
struct EnableIf {};
template<class T>
struct EnableIf<true,T> {
typedef T type;
};
I now expect that only EnableIf with true to have a "namespace type member" (if I may) called "type" which is whatever the template T is. This seems to work.
I understand that the false/default case should "silently fail" when I try and access EnableIf<false,T>::type because of the absence of type
I'm pretty sure everything so far is right, but I'm just being verbose.
My test case
I've chosen a list as my testing ground (again not using the standard set because I am investigating) Usually I use a class hierarchy to do this, I'd have a list that could do nothing more than act as an array, the only extra member would be int find(T*); because a T* is a T's identity.
Then I'd extend that to have a int find(T&); int find(T&,int) and int count(T&) and this'd use == to compare Ts. This is what I mean by leaving it up to the user, they could chose the list they wanted based on what /they/ knew about the type.
I want to use EnableIf (later std::enable_if when I feel more confident) to do this instead, that way when the template is stamped out functionality is only enabled if the type is able to be used that way.
List definition
template<class T>
class List {
public:
typedef typename T::hasEquality hasEquality;
virtual ~List() {}
virtual T& operator[](int index) =0;
virtual const T& operator[](int index) const =0;
virtual void append(T*) =0;
virtual int length() const =0;
virtual
typename EnableIf<hasEquality::value, bool>::type
operator==(const List<T>& rhs) const {
if(length() == rhs.length()) {
for(int k=0;k!=length();k++) {
if(!((*this)[k] == rhs[k])) {
return false;
}
}
return true;
} else {
return false;
}
}
virtual
typename EnableIf<T::hasEquality::value, int>::type
count(const T& what) const =0;
};
It's a list not a set so order matters. You can see that this should make hasEquality transitive in the sense that:
if T has the concept of equality than a list of T has the concept of equality also
I then go on to implement a singly-linked-list.
Testing types
class A {
public:
A(int value) { val = value; }
typedef True hasEquality;
bool operator==(const A& rhs) const {
if(val == rhs.val) {
return true;
}
return false;
}
private:
int val;
};
class B {
public:
typedef False hasEquality;
};
Results
int main(int,char**) {
LinkedList<A> listA;
listA.append(new A(6));
A a(6);
std::cout<<"there are "<<listA.count(a)<<" of them\n";
return 0;
}
As you'd expect this works. My first test initially included B but that causes problems.
int main(int,char**) {
LinkedList<A> listA;
listA.append(new A(6));
A a(6);
std::cout<<"there are "<<listA.count(a)<<" of them\n";
LinkedList<B> listB;
return 0;
}
This does not, it fails with:
src/main.cpp: In instantiation of ‘class List<B>’:
src/main.cpp:77:7: required from ‘class LinkedList<B>’
src/main.cpp:176:16: required from here
src/main.cpp:59:2: error: no type named ‘type’ in ‘struct EnableIf<false, bool>’
operator==(const List<T>& rhs) const {
^
src/main.cpp:73:3: error: no type named ‘type’ in ‘struct EnableIf<false, int>’
count(const T& what) const =0;
^
src/main.cpp: In instantiation of ‘class LinkedList<B>’:
src/main.cpp:176:16: required from here
src/main.cpp:134:3: error: no type named ‘type’ in ‘struct EnableIf<false, int>’
count(const T& what) const {
^
make: *** [build/main.o] Error 1
For some reason it puts the error marker after the typename line, it is unhappy everywhere I use an EnableIf with false
I am really not sure why this is, it is right, there is no type, but this is by design!
Research
http://en.cppreference.com/w/cpp/types/enable_if
To quote that:
template<bool B, class T = void>
struct enable_if {};
template<class T>
struct enable_if<true, T> { typedef T type; };
Mine differs only by name and the default T being void, adding this to mine (as I expected) does not fix the problem.
http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/SFINAE
http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/enable-if
Confirm my thoughts.
Bonus questions
constexpr and static
Initially I tried struct False { constexpr bool operator==(bool what) { return !what; } }; and the same for True;
But this did not work, and I cannot use the word "static" to qualify operator==, but I could have used a method called constexpr static bool is(bool what); for the same effect, why doesn't constexpr imply static?
In my mind constexprs never really exist, and the design is sort of like the opposite of virtual, there's nothing that says you cannot use an instance to call a static method, I've just checked C++03 standard, section 9.4 confirms this, has this changed?
SFINAE
Would it be possible to use SFINAE to assume False when hasMember has not been defined? I understand this wont work for the fundamental types and such, this is an experiment. I will not use these techniques in production stuff until I am confident.
The problem is here:
virtual
typename EnableIf<T::hasEquality::value, int>::type
count(const T& what) const =0;
You hit another example where generic programming (templates) and object oriented programming styles conflict.
SFINAE is a metaprogramming technique that works with templates. Despite the appearence (the use of T), the function declared above is not a template. It's a normal function inside a template class. The template type parameter T is a parameter of List and not of count.
For instance, the following is an example of SFINAE:
template<class T>
class List {
public:
template<class T>
class List {
public:
// ...
template <typename U>
typename EnableIf<std::is_same<U, T>::value && U::hasEquality::value, int>::type
count(const U& what) const { std::cout << "1\n"; }
template <typename U>
typename EnableIf<std::is_same<U, T>::value && !U::hasEquality::value, int>::type
count(const U& what) const { std::cout << "2\n"; }
};
};
int main() {
A a(1);
B b;
List<A> la;
la.count(a); // outputs 1
List<B> lb;
lb.count(b); // ouputs 2
}
Notice that the two counts are now a templates (parametrized on U). Both are active only if T is the same type as U. This is a workaround to accept T only (it's not perfect, for instance, it discards implicit conversions). The first requires, in addition, that U::hasEquality::value == true and the second requires the opposite.
The key point here is that SFINAE works on templates.
But as you can see I changed your design and made count non virtual. Unfortunately, you cannot make the count functions above virtual because template functions cannot be virtual.
The basic issue is as follows. Template functions are instanciated only when they are called. So when the compiler parses List (my version) it doesn't know yet all the instantiations of count that are going to exist.
For each virtual function there should be an entry in the virtual table and when the compiler parses List it must know how many entries there are in the virtual table.
Hence, on one hand, when parsing List the compiler doesn't know the number of template instancitaions and, on the other hand, it must know the number of virtual functions. The conclusion is that template functions cannot be virtual.
SFIANE only applies to template argument deduction and overload resolution of functions. For classes you can instead use template specialization. In your case you could do template specialization something like this (no EnableIf required):
template <typename T, typename = typename T::hasEquality>
class List;
template <typename T>
class List<T, False> {
// no operator==
};
template <typename T>
class List<T, True> {
public:
// ...
bool operator==(const List<T,True>& rhs) const {
// ...
}
// ...
};
As to your constexpr question, you can have constexpr constructors that create compile time object you can then call constexpr member functions on that will run at compile time, so it doesn't make sense for constexpr to imply static.
It's too late for me to be answering questions it seems. You can use SFINAE to conditionally enable a member function, it just has to be a template function. So you can change your operator== to
template <typename = typename EnableIf<T::hasEquality::value, void>::type>
bool operator==(const List<T>& rhs) const {
// ...
}
and it should work.