Only overload operator if template argument does - c++

Given a template class A with a single template argument T, is it possible to only overload operators in A which are available for type T? For example:
template <typename T>
class A
{
public:
#if hasOperator(T, +=)
T& operator +=(const T &rhs)
{
mValue += rhs;
return mValue;
}
#endif
private:
T mValue;
}
int main()
{
A<int> a;
a += 8; //+= will forward to the += for the int
struct Test { /*no operators defined*/ };
A<Test> b; //+= is not implemented since Test does not implement +=
}
I'm writting a generic wrapper class that needs to behave exactly like the template type. So if T has operator +=, A will (at compile time) overload += accordingly. Yes, I could go ahead and just implement every operator in A, but then the compiler will error when T doesn't have a certain operator. At first I though template specialization might be the answer, but that would require a specialization for every type. While this could work and be a lot of typing, it wont because A needs to work with any type (not just what is specialized).

Use expression SFINAE to drop your operator+ from the overload resolution set unless T defines operator+
template <typename T>
class A
{
private:
T mValue;
public:
template<typename U=T>
auto operator +=(const U &rhs)
-> decltype(mValue += rhs)
{
mValue += rhs;
return mValue;
}
};
Live demo

I will give 3 solutions in decreasing complexity and utility. The last solution is the simplest, and least complex.
A small, if useful, metaprogramming library:
template<class...>struct types{using type=types;};
namespace details {
template<template<class...>class Z, class types, class=void>
struct can_apply : std::false_type {};
template<template<class...>class Z, class...Ts>
struct can_apply<Z,types<Ts...>,std::void_t<Z<Ts...>>> :
std::true_type
{};
}
template<template<class...>class Z, class...Ts>
using can_apply = details::can_apply<Z,types<Ts...>>;
A trait for the result of +=:
template<class Lhs, class Rhs>
using plus_equal_result = decltype(std::declval<Lhs>()+=std::declval<Rhs>());
template<class Lhs, class Rhs>
using can_plus_equal = can_apply< plus_equal_result, Lhs, Rhs >;
template<class T>
using can_self_plus_equal = can_plus_equal< T&, T const& >;
which gives us some nice traits that return true type or false type depending on if += is valid.
template<class A, class T, bool b = can_self_plus_equal<T>{}>
struct A_maybe_plus_equal {};
template<class A, class T>
struct A_maybe_plus_equal<A, T, true> {
A& self() { return *static_cast<A*>(this); }
A& operator+=( T && t )
{
self().mResult += std::move(t);
return self();
}
template<class U>
std::enable_if_t<can_plus_equal<T&,U>{},A&> operator+=( U && u )
{
self().mResult += std::forward<U>(u);
return self();
}
};
which gives us a += iff we pass true.
template <class T>
class A:
public A_maybe_plus_equal<A<T>, T>
{
friend class A_maybe_plus_equal<A<T>, T>;
public:
// nothing needed
private:
T mValue;
};
which gives you a += overload that takes a const T& or a T&& or a U&& on the right hand side if and only if T& += T const& is a valid expression.
This is the "perfect" solution, but it is complex.
Note that each operator can be done separately, so you don't have a combinatorial explosion of specializations.
Now, the there is an easier option. It has the downside that it doesn't support {} based construction on the right hand side, and under some readings of the standard it is illegal.
It is, however, still SFINAE friendly:
template <typename T>
class A {
public:
template<class U>
auto operator +=(U&&rhs)
-> decltype( (std::declval<T&>()+=std::declval<U&&>()),void(),A& )
// or std::enable_if_t<can_plus_equal<T&,U>{},A&>
{
mValue += std::forward<U>(rhs);
return *this;
}
private:
T mValue;
};
This can be folded into the above option, and give both {} and perfect forwarding syntax. I find that the T const& can be dropped if you have a template perfect forwarder.
The reason why this is technically undefined behavior is that the standard mandates that all template functions have at least one set of arguments that would render their body able to compile. In a given class instance, the template += of the above may have no such set of type arguments, which makes your program ill-formed with no diagnostic required (ie, UB).
There is another rule that member functions of template classes don't get instantiated unless called. Some argue that this other rule supersedes the one I mentioned in the last paragraph.
Another argument is that the method may be legal so long as there is some mixture of template arguments to the enclosing class(es) and to the template method itself that lead to it being instantiatable. I'd guess that this is what the standard committee intended, but I don't know how to read the standard to get this result.
This argument also applies to the plus_equal function in answer #1. That implementation need not be as simple. In addition, #1 provides {} based += syntax, which is a practical reason to use it. This concern -- that the program is technically ill-formed -- is academic, as all compilers I have used have no problems with this construct.
The paragraph three above this one gives us our final option. Do nothing.
template <typename T>
class A {
public:
A& operator +=(const T &rhs) {
mValue += rhs;
return *this;
}
private:
T mValue;
};
which means that you cannot SFINAE test that += doesn't work, but so long as you don't call += it "works". This is how vector's operator< works, for example. This is a lower "quality" solution, and cases of this in the standard library tend to be repaired over time.
However, as a first pass, this last choice is usually best. Only if you expect SFINAE requirements are the above hoops worthwhile.
Ultimately, C++1z is introducing concepts. I believe concepts will make this problem much easier, as eliminating overloads from consideration based on the type arguments of the enclosing class is a perennial problem in std.

You don't actually have to do anything. Individual member functions of a template class won't get instantiatiated until use. You say:
but then the compiler will error when T doesn't have a certain operator.
But isn't that clearer than having it error when A<T> doesn't? If you have:
template <typename T>
class A
{
public:
A& operator +=(const T &rhs)
{
mValue += rhs;
return *this;
}
A& operator-=(const T &rhs)
{
mValue -= rhs;
return *this;
}
// etc
private:
T mValue;
};
Then this will just work:
int main() {
A<int> a;
a += 8; //+= will forward to the += for the int
struct Test {
Test& operator-=(const Test& ) { return *this; }
};
A<Test> b;
b -= Test{}; // totally fine
b += Test{}; // error: no match for +=
// (operand types are 'main()::Test' and 'const main()::Test')
}

Related

SFINAE User-Defined-Conversion Operator

I'm trying to do a templated user-defined-conversion that uses design by introspection.
In C++20 I can do the following:
template<typename T>
operator T() const
{
if constexpr( requires { PFMeta<T>::from_cursor(*this); } )
{
return PFMeta<T>::from_cursor(*this);
}
else
{
T x;
std::memcpy(&x, &data, sizeof(x));
return x;
}
}
i.e. if PFMeta<T>::from_cursor(const struct PFCursor&) is defined then use it else do a memcpy. (longer example here in godbolt: https://godbolt.org/z/abed1vsK3)
I love this approach but unfortunately this library will need to work on C++17 too.
So I've been trying SFINAE as an alternative to the concepts but it's very tricky.
I finally managed to get something similar but with a templated method as rather than the user-defined-conversion operator itself:
template<typename T, typename = void>
struct has_from_cursor : std::false_type { };
template<typename T>
struct has_from_cursor<T, decltype( PFMeta<T>::from_cursor(std::declval<struct PFCursor>()), void() ) > : std::true_type { };
// ...
template<class T>
std::enable_if_t<has_from_cursor<T>::value, T> as() const
{
return PFMeta<T>::from_cursor(*this);
}
template<class T>
std::enable_if_t<!has_from_cursor<T>::value, T> as() const
{
T x;
std::memcpy(&x, &data, sizeof(x));
return x;
}
I tried the following which compiles but does not work (I can't cast with it):
template<typename T>
operator std::enable_if_t<has_from_cursor<T>::value, T>() const
{
return PFMeta<T>::from_cursor(*this);
}
template<typename T>
operator std::enable_if_t<!has_from_cursor<T>::value, T>() const
{
T x;
std::memcpy(&x, &data, sizeof(x));
return x;
}
Longer example here: https://godbolt.org/z/5r9Mbo18h
So two questions:
Can I do effectively what I can do with concepts with just SFINAE in C++17 for the user defined conversion operator?
Is there a simpler way to do the as approach than the type traits & enable_if? Ideally I'd like to do something like define the default templated method and then have a specialisation to be preferred if the condition is there (i.e. there is a meta class with a static member function defined).
Thanks!
Can I do effectively what I can do with concepts with just SFINAE in C++17 for the user defined conversion operator?
Consider that C++17 support if constepr. Given that you've developed a has_from_cursor custom type traits that inherit from std::true_type or from std::false_type, you can use it for if constexp.
I mean (caution: code not tested)
template<typename T>
operator T() const
{ // .............VVVVVVVVVVVVVVVVVVVVVVVVV
if constexpr( has_from_cursos<T>::value )
{
return PFMeta<T>::from_cursor(*this);
}
else
{
T x;
std::memcpy(&x, &data, sizeof(x));
return x;
}
}
Is there a simpler way to do the as approach than the type traits & enable_if? Ideally I'd like to do something like define the default templated method and then have a specialisation to be preferred if the condition is there (i.e. there is a meta class with a static member function defined).
I suppose you can try tag-dispatching...
I mean something as calling 'as()', from operator T() with an additional int argument
template<typename T>
operator T() const
{ return as<T>(0); } // <-- call as() with a int
where there is a as() specific for from_cursos class enabled, that receive an unused int and is simply SFINAE enabled/disabled through decltype()
template <typename T> // ........accept an int; best match
decltype(PFMeta<T>::from_cursor(*this)) as (int) const
{ return PFMeta<T>::from_cursor(*this); }
and a generic as(), receiving a long
template <typename T>
T as (long) const // <-- accept a long; worst match
{
T x;
std::memcpy(&x, &data, sizeof(x));
return x;
}
The trick is the unused argument: a int.
When the specialized as() is enabled, if preferred because accept a int so is a better match.
When the specialized as() is disabled, remain the generic as() as better-than-nothig match.

Overloading Class Methods vs. Templates

I'm looking for an elegant way (defined by minimal repeated code) to overload an operator to do the following:
I have a template class BaseSignal which overloads the += operator, and I would like to be able to accept many different types. For example, for a double, the code looks like
template <class T>
class BaseSignal
{
public:
....
// Self-increment
BaseSignal<T>& operator+=(const double& rhs)
{
T rval;
this->Get(&rval);
this->Set(rval + static_cast<T>(rhs));
return *this;
}
....
What I've been doing is repeating this body of code for int, long, etc. Since the value rhs is only used in the line static_cast<T>(rhs), I am repeating code while only changing the input parameter type.
So I could template this, e.g.
template <class T, class U>
class BaseSignal
{
public:
....
// Self-increment
BaseSignal<T>& operator+=(const U& rhs)
{
T rval;
this->Get(&rval);
this->Set(rval + static_cast<T>(rhs));
return *this;
}
....
But then it looks like I lose the "overloading" aspect in that the compiler would automatically select the correct method for me (it would also only work for the original U type that was instantiated).
I realize that I am trying to achieve some untyped language behavior with C++, which may not be the smartest thing, I am just trying to add some intelligence to a few commonly-used classes so that subsequent code will become much easier to write.
Thank you in advance.
You might do
template <class T>
class BaseSignal
{
public:
// Self-increment
template <class U>
BaseSignal<T>& operator+=(const U& rhs)
{
T rval;
this->Get(&rval);
this->Set(rval + static_cast<T>(rhs));
return *this;
}
};

Curiously Recurring Pattern and Sfinae

I've been meaning to implement total ordering via SFINAE and the Curiously Recurring Template Pattern idiom for a while now. The general idea is as follows:
Define templates that check for relational operators (<, >, etc.)
Define base classes that define total ordering operators.
Define a base class from operator detection.
Inherit from base class.
For simplicity, I have ignored the == and != operators in this example.
Relational Operator Detection
I first define classes to statically check whether the class defines a specific feature. For example, here I detect the presence of the less-than operator, or operator<.
template <typename T>
class has_less
{
protected:
template <typename C> static char &test(decltype(std::declval<C>() < std::declval<C>()));
template <typename C> static long &test(...);
public:
enum {
value = sizeof(test<T>(0)) == sizeof(char)
};
};
template <typename T>
constexpr bool has_less_v = has_less<T>::value;
Total Ordering
I then define classes that implement total ordering from a given operator, for example, to define total ordering from the less-than operator, I would use the following:
template <typename T>
struct less_than_total
{
bool operator>(const T &t) { return t < static_cast<T&>(*this); }
bool operator>=(const T &t) { return !(static_cast<T&>(*this) < t); }
bool operator<=(const T &t) { return !(t < static_cast<T&>(*this)); }
};
Base Class
I then define a single base class that creates a typedef to implement the remaining operators by detecting the implemented operator.
template <bool B, typename T, typename F>
using conditional_t = typename std::conditional<B, T, F>::type;
template <typename T>
using total_ordering = conditional_t< // has_less
has_less_v<T>,
less_than_total<T>,
conditional_t< // has_less_equal
has_less_equal_v<T>,
less_equal_total<T>,
conditional_t< // has_greater
has_greater_v<T>,
greater_total<T>,
conditional_t< // has_greater_equal
has_greater_equal_v<T>,
greater_equal_total<T>,
symmetric<T> // symmetry
> // has_greater_equal
> // has_greater
> // has_less_equal
>; // has_less
Inheritance
All of these steps, individually, work. However, when I actually inherit from the base class using the curiously recurring pattern, the resulting class implements only one of these operators and the detection algorithms fail.
Example
I've boiled down the issue to a minimal example consisting of the core parts: operator detection (has_less, has_greater), total ordering implementation (total), a simplified base class (total), and a simple struct using these relational operators (A).
#include <type_traits>
// DETECTION
template <typename T>
class has_less
{
protected:
template <typename C> static char &test(decltype(std::declval<C>() < std::declval<C>()));
template <typename C> static long &test(...);
public:
enum {
value = sizeof(test<T>(0)) == sizeof(char)
};
};
template <typename T>
class has_greater
{
protected:
template <typename C> static char &test(decltype(std::declval<C>() > std::declval<C>()));
template <typename C> static long &test(...);
public:
enum {
value = sizeof(test<T>(0)) == sizeof(char)
};
};
// TOTAL ORDERING
template <typename T>
struct less_than_total
{
bool operator>(const T &t) { return t < static_cast<T&>(*this); }
bool operator>=(const T &t) { return !(static_cast<T&>(*this) < t); }
bool operator<=(const T &t) { return !(t < static_cast<T&>(*this)); }
};
template <typename T>
struct symmetry
{};
template <bool B, typename T, typename F>
using conditional_t = typename std::conditional<B, T, F>::type;
template <typename T>
struct total: conditional_t<
has_less<T>::value,
less_than_total<T>,
symmetry<T>
>
{};
// TEST
struct A: total<A>
{
bool operator<(const A &r)
{
return true;
}
};
int main(void)
{
static_assert(has_less<A>::value, "");
static_assert(has_greater<A>::value, "");
return 0;
}
Ideally, this example would compile, however, I get:
$ clang++ a.cpp -o a -std=c++14
a.cpp:79:5: error: static_assert failed ""
static_assert(has_less<A>::value, "");
^ ~~~~~~~~~~~~~~~~~~
a.cpp:80:5: error: static_assert failed ""
static_assert(has_greater<A>::value, "");
Unfortunately, the base class is not detecting the operators during inheritance, and SFINAE does not detect the less than or greater than operators in the resulting class.
Question and Follow-Up
I would like to know why this fails, since I have been doing member-function detection and member-type detection for a long time with the curiously recurring pattern without problem. And assuming there is no direct issue with my code, are there any work-arounds to implement total ordering in such a manner?
Edit
I'm able to achieve a subset of what I want using std::enable_if. In this case, the only simple answer is to implement everything in terms of operator< and then implement total ordering from that operator.
template <typename T>
struct total
{
template <typename U = T>
typename std::enable_if<has_less<U>::value, bool>::type
bool operator>(const T &l, const T &r) { return r < t; }
};
If would still like an answer for why my operator detection via SFINAE fails during inheritance, but succeeds for inherited methods.
The main problem with this is that A is an incomplete type when has_less<A> is instantiated (during the instantiation of total<A> as A's base class) -- at this point, the compiler doesn't yet know that A has an operator <.
So, has_less<A> is instantiated with its value == 0, and symmetry<A> is selected for total<A>'s base class -- so A never gets any of its additional operators.
After all this is decided, the compiler sees the definition of A::operator <, which it adds to A. After this, A is complete.
So we know why static_assert(has_greater<A>::value, ""); fails, but shouldn't we expect static_assert(has_less<A>::value, ""); to succeed? After all, now A has a less-than operator. The thing is, has_less<A> was already instantiated with the incomplete A, with value == 0 -- even though A has changed, there is no mechanism for updating previously instantiated compile-time values. So this assertion fails too, even though it looks like it should succeed.
To show that this is the case, make a copy of has_less, name it has_less2, and change the static assertion to static_assert(has_less2<A>::value, "");. Because has_less2<A> is instantiated after A gets its less-than operator, this assertion succeeds.
One way you can make the code succeed is to forward-declare A and declare a global operator <, for comparing two A objects, so that the compiler knows about this operator before A's base class is worked out. Something like this:
struct A;
bool operator < (const A &lh, const A& rh);
struct A : total<A> {
friend bool operator < (const A &lh, const A& rh) {
return true;
}
};
I understand that this is not really what you want, however -- it would be much nicer if the CRTP setup happened automatically, without any special accommodation required in the derived class. But this might still give you some insight which can help lead you to a proper solution. I will also think about this some more, and if I come up with anything, I'll update this answer.
One more thing: Comparison member functions should be const qualified. Like
bool operator>(const T &t) const { ...
This is really important and will prevent a lot of non-obvious problems compiling code which uses these classes later on.

C++ templates: conditionally enabled member function

I'm creating a very small C++ project, and I'd like to create a simple vector class for my own needs. The std::vector template class will not do. When the vector class is comprised of chars (i.e. vector<char>), I'd like it to be able to be compared to a std::string. After a bit of messing around, I wrote code that both compiles and does what I want. See below:
#include <string>
#include <stdlib.h>
#include <string.h>
template <typename ElementType>
class WorkingSimpleVector {
public:
const ElementType * elements_;
size_t count_;
// ...
template <typename ET = ElementType>
inline typename std::enable_if<std::is_same<ET, char>::value && std::is_same<ElementType, char>::value, bool>::type
operator==(const std::string & other) const {
if (count_ == other.length())
{
return memcmp(elements_, other.c_str(), other.length()) == 0;
}
return false;
}
};
template <typename ElementType>
class NotWorkingSimpleVector {
public:
const ElementType * elements_;
size_t count_;
// ...
inline typename std::enable_if<std::is_same<ElementType, char>::value, bool>::type
operator==(const std::string & other) const {
if (count_ == other.length())
{
return memcmp(elements_, other.c_str(), other.length()) == 0;
}
return false;
}
};
int main(int argc, char ** argv) {
// All of the following declarations are legal.
WorkingSimpleVector<char> wsv;
NotWorkingSimpleVector<char> nwsv;
WorkingSimpleVector<int> wsv2;
std::string s("abc");
// But this one fails: error: no type named ‘type’ in ‘struct std::enable_if<false, bool>’
NotWorkingSimpleVector<int> nwsv2;
(wsv == s); // LEGAL (wanted behaviour)
(nwsv == s); // LEGAL (wanted behaviour)
// (wsv2 == s); // ILLEGAL (wanted behaviour)
// (nwsv2 == s); // ??? (unwanted behaviour)
}
I believe I understand why the error is occurring: The compiler creates the class definition for NotWorkingSimpleVector<int>, and then the return type of my operator== function becomes:
std::enable_if<std::is_same<int, char>::value, bool>::type
which then becomes:
std::enable_if<false, bool>::type
which then yields an error: there is no type member of std::enable_if<false, bool>, which is indeed the entire point of the enable_if template.
I have two questions.
Why won't SFINAE simply disable the definition of operator== for NotWorkingSimpleVector<int>, like I want it to? Is there some compatibility reason for this? Are there other use-cases I'm missing; does a reasonable counter-argument for this behaviour exist?
Why does the first class (WorkingSimpleVector) work? It seems to me that the compiler 'reserves judgement': Since the 'ET' parameter is not yet defined, it gives up trying to tell if operator== can exist. Are we relying on the compilers 'lack of insight' to allow this kind of conditionally enabled function (even if this 'lack of insight' is acceptable by the C++ specification)?
SFINAE works in a template function. In the context of template type substitution, substitution failure in the immediate context of the substitution is not an error, and instead counts as substitution failure.
Note, however, that there must be a valid substitution or your program is ill formed, no diagnostic required. I presume this condition exists in order that further "more intrusive" or complete checks can be added to the language in the future that check the validity of a template function. So long as said checks are actually checking that the template could be instantiated with some type, it becomes a valid check, but it could break code that expects that a template with no valid substitutions is valid, if that makes sense. This could make your original solution an ill formed program if there is no template type you could pass to the operator== function that would let the program compile.
In the second case, there is no substitution context, so SFINAE does not apply. There is no substitution to fail.
Last I looked at the incoming Concepts proposal, you could add requires clauses to methods in a template object that depend on the template parameters of the object, and on failure the method would not be considered for overload resolution. This is in effect what you want.
There is no standards-compliant way to do this under the current standard. The first attempt is one that may people commonly do, and it does compile, but it is technically in violation of the standard (but no diagnostic of the failure is required).
The standards compliant ways I have figured out to do what you want:
Changing one of the parameters of the method to be a reference to a never-completed type if your condition fails. The body of the method is never instantiate if not called, and this technique prevents it from being called.
Using a CRTP base class helper that uses SFINAE to include/exclude the method depending on an arbitrary condition.
template <class D, class ET, class=void>
struct equal_string_helper {};
template <class D, class ET>
struct equal_string_helper<D,ET,typename std::enable_if<std::is_same<ET, char>::value>::type> {
D const* self() const { return static_cast<D const*>(this); }
bool operator==(const std::string & other) const {
if (self()->count_ == other.length())
{
return memcmp(self()->elements_, other.c_str(), other.length()) == 0;
}
return false;
}
};
where we do this:
template <typename ElementType>
class WorkingSimpleVector:equal_string_helper<WorkingSimpleVector,ElementType>
We can refactor the conditional machinery out of the CRTP implementation if we choose:
template<bool, template<class...>class X, class...>
struct conditional_apply_t {
struct type {};
};
template<template<class...>class X, class...Ts>
struct conditional_apply_t<true, X, Ts...> {
using type = X<Ts...>;
};
template<bool test, template<class...>class X, class...Ts>
using conditional_apply=typename conditional_apply_t<test, X, Ts...>::type;
Then we split out the CRTP implementation without the conditional code:
template <class D>
struct equal_string_helper_t {
D const* self() const { return static_cast<D const*>(this); }
bool operator==(const std::string & other) const {
if (self()->count_ == other.length())
{
return memcmp(self()->elements_, other.c_str(), other.length()) == 0;
}
return false;
}
};
then hook them up:
template<class D, class ET>
using equal_string_helper=conditional_apply<std::is_same<ET,char>::value, equal_string_helper_t, D>;
and we use it:
template <typename ElementType>
class WorkingSimpleVector: equal_string_helper<WorkingSimpleVector<ElementType>,ElementType>
which looks identical at point of use. But the machinery behind was refactored, so, bonus?
Templating operator== basically makes it uncallable. You'd have to explicitly do:
myvec.operator==<char>(str);
The simplest solution might just to add a non-member function:
bool operator==(const WorkingVector<char>& vec, const std::string& s);
bool operator==(const std::string& s, const WorkingVector<char>& vec);
To enable SFINAE and to keep it as a member function, you'd have to forward your type to something else:
bool operator==(const std::string& s) const {
return is_equal<ElementType>(s);
}
template <typename T> // sure, T == ET, but you need it in this context
// in order for SFINAE to apply
typename std::enable_if<std::is_same<T, char>::value, bool>::type
is_equal(const std::string& s) {
// stuff
}
Simple answer
This answer bares parallels to Yakk's answer but is not quite as useful (Yakk's one supports arbitrary if-expressions). It is, however, quite a bit simpler and easier to understand.
template <typename ThisClass, typename ElementType>
class WorkingSimpleVector_Base {
};
template <typename ThisClass>
class WorkingSimpleVector_Base<ThisClass, char> {
private:
ThisClass * me() { return static_cast<ThisClass*>(this); };
const ThisClass * me() const { return static_cast<const ThisClass*>(this); };
public:
bool operator==(const std::string & other) const {
if (me()->count_ == other.length())
{
return memcmp(me()->elements_, other.c_str(), other.length()) == 0;
}
return false;
}
};
template <typename ElementType>
class WorkingSimpleVector : public WorkingSimpleVector_Base<WorkingSimpleVector<ElementType>, ElementType> {
public:
const ElementType * elements_;
size_t count_;
};
This works by utilising template specialization for the 'if statements' we want. We base the class off of WorkingSimpleVector_Base, which then only contains functions if the ElementType value is char (second definition of WorkingSimpleVector_Base). Otherwise, it has no functions at all (first definition of WorkingSimpleVector_Base). The ThisClass parameter is what makes this a "CRTP" (Curiously Recurring Template Pattern). It allows the template to access the child class's fields through the use of the me() function. Remember that the template is no different from any other class so it won't have access to private members (unless the child class declares it as a friend).
Modified version of Yakk's answer explained
This first thing he/she declares is a helper template that does this whole conditional declaration for us:
template<bool, template<class...>class X, class...>
struct conditional_apply_t {
struct type {};
};
template<template<class...>class X, class...Ts>
struct conditional_apply_t<true, X, Ts...> {
using type = X<Ts...>;
};
template<bool test, template<class...>class X, class...Ts>
using conditional_apply=typename conditional_apply_t<test, X, Ts...>::type;
Variadic templates are scary, and I think they can be removed in this case. Let's simplify this to:
template<bool, class X>
struct conditional_apply_t {
struct type {};
};
template<class X>
struct conditional_apply_t<true, X> {
using type = X;
};
template<bool test, class X>
using conditional_apply=typename conditional_apply_t<test, X>::type;
conditional_apply_t's type type is an empty struct if the condition test is not true (see first definition of conditional_apply_t). If it is true, then the type type is the value of X. The definition of conditional_apply just removes the need for us to write ::type at the end of conditional_apply_t<...> every time we use this construct.
Next, we define a template that implements the behaviour we want
template <class D>
struct equal_string_helper_t {
D const* self() const { return static_cast<D const*>(this); }
bool operator==(const std::string & other) const {
if (self()->count_ == other.length())
{
return memcmp(self()->elements_, other.c_str(), other.length()) == 0;
}
return false;
}
};
In this case, the D parameter is what gives us the "CRTP" (Curiously Recurring Template Pattern). See the above "Simple Answer" for more details on why this is important.
Next, we declare a type that only has this operator== function if a condition is satisfied:
template<class D, class ET>
using equal_string_helper=conditional_apply<std::is_same<ET,char>::value, equal_string_helper_t<D>>;
So, the equal_string_helper<D,ET> type is:
An empty struct when ET != char
equal_string_helper_t<D> when ET == char
Finally, after all this, we can create the class we wanted with the following:
template <typename ElementType>
class WorkingSimpleVector : public equal_string_helper<WorkingSimpleVector<ElementType>, ElementType> {
public:
const ElementType * elements_;
size_t count_;
};
Which works as required.

C++ STL fails to find comparator for nested class

I expected this code to work, but it fails to compile with GCC. It does compile if you lift the inner class out.
#include <algorithm>
template <typename T>
struct Outer
{
struct Inner
{
int x;
};
Inner vec[3];
};
template <typename T>
bool operator <(const typename Outer<T>::Inner& lhs, const typename Outer<T>::Inner& rhs)
{
return lhs.x < rhs.x;
}
int main()
{
Outer<int> out;
Outer<int>::Inner in;
std::lower_bound(out.vec, out.vec + 3, in);
}
GCC 4.4 has this to say:
...
bits/stl_algo.h:2442: error: no match for ‘operator<’ in ‘* __middle < __val’
GCC 4.7 prints a lot more stuff, including the above, ending with this:
...
bits/stl_algobase.h:957:4: note: couldn't deduce template parameter ‘T’
I'm willing to believe it's not well-formed C++, but why not?
The problem as you mentioned is that the compiler couldn't deduce template parameter T. That is because typename Outer::Inner is a nondeduced context context for T.
When a template parameter is used only in a nondeduced context, it's not taken into consideration for template argument deduction. The details are at section 14.8.2.4 of the C++ Standard (2003).
Why? If you specialize Outer as:
template <>
struct Outer<int>
{
struct Inner
{
double y;
};
Inner vec[3];
};
There is no way for the compiler to deduce if Outer::Inner should reference this definition or the previous one.
Now, onto solutions. There are a couple of possible resolutions:
Move the operator< inside Inner and make it a friend function.
Define operator< inside Inner like M M. suggested.
Suggested by Johannes Schaub - litb: Derive inner from a CRTP base parameterized by inner. Then make the parameter type the CRTP class and cast the parameter reference to the derived inner class, the type of which is given by the template argument you deduce.
I tried out the CRTP approach since it sounds so cool!:
template <typename Inner>
struct Base
{
};
template <typename T>
struct Outer
{
struct Inner : Base<Inner>
{
T x;
};
Inner vec[3];
};
template <typename T>
bool operator< (const Base<T>& lhs, const Base<T>& rhs)
{
return static_cast<const T&>(lhs).x < static_cast<const T&>(rhs).x;
}
Related answers: 1, 2, 3
Here's another workaround.
Why don't you use a custom comparer?
template <typename T>
struct Comparer
{
bool operator()(const typename Outer<T>::Inner& lhs, const typename Outer<T>::Inner& rhs)
{
return lhs.x < rhs.x;
}
};
int main()
{
Outer<int> out;
Outer<int>::Inner in;
std::lower_bound(out.vec, out.vec + 3, in, Comparer<int>());
}
Hope this works for you.
If you overload a specific operator< for int the problem will vanish:
bool operator<(const typename Outer<int>::Inner& lhs,
const typename Outer<int>::Inner& rhs)
{
return lhs.x < rhs.x;
}
The simpler solution is defining operator< inside Inner:
template<typename T>
struct Outer
{
struct Inner
{
int x;
bool operator<(const Inner& obj) const
{
return x < obj.x;
}
};
Inner vec[3];
};
Also, it's just a quick solution. And my answer is not the why compiler can not find operator< in nested situation in template mode.
Mar0ux's answer is pretty good. You can find additional information here:
Stephan T. Lavavej: Core C++, 2 of n
You should watch the whole video series - it contains a lot of useful information but you might start from minute 34 or so to get your question answered. Stephen mentions one basic rule to keep in mind:
:: is a brick wall for template argument deduction, i.e. a template argument T on the left side can't be deduced.