EDIT: Prolog: I'm a victim of my own ignorance and also of late-night coding.
I'm writing a templated class using template template. It has an iterator, which means that I need to provide an appropriately templated operator==(). This is where I'm having trouble.
Representative code sample follows:
#include <iostream>
#include <typeinfo>
using namespace std;
namespace detail {
template <typename T> class foo {};
template <typename T> class bar {};
}
template <template<class> class A, template<class> class B>
struct basic_thing {
template <typename T> using target_type = A<B<T>>;
target_type<float> fmember;
target_type<int> imember;
struct iterator {
bool equal (const iterator& other) { return true; }
};
iterator begin () { return iterator{}; }
iterator end () { return iterator{}; }
};
template <template<class> class A, template<class> class B>
bool operator== (const typename basic_thing<A, B>::iterator& lhs, const typename basic_thing<A, B>::iterator& rhs) {
return lhs.equal(rhs);
}
int main ()
{
using Thing = basic_thing<detail::foo, detail::bar>;
Thing t;
cout << typeid(t.fmember).name() << endl;
cout << typeid(t.imember).name() << endl;
bool b = (t.begin() == t.end());
return 0;
}
My goal here is to provide a composable way to define basic_thing::target_type, and this pattern works for that purpose. But, I'm stuck at how to declare operator==() for basic_thing::iterator. Either this isn't very straightforward, or there's something obvious that I'm missing. (Likely the latter.)
g++-7.4.0 with -std=c++11 produces the following:
foo.cc: In function 'int main()':
foo.cc:39:23: error: no match for 'operator==' (operand types are 'basic_thing<detail::foo, detail::bar>::iterator' and 'basic_thing<detail::foo, detail::bar>::iterator')
bool b = (t.begin() == t.end());
~~~~~~~~~~^~~~~~~~~~
foo.cc:27:6: note: candidate: template<template<class> class A, template<class> class B> bool operator==(const typename basic_thing<A, B>::iterator&, const typename basic_thing<A, B>::iterator&)
bool operator== (const typename basic_thing<A, B>::iterator& lhs, const typename basic_thing<A, B>::iterator& rhs) {
^~~~~~~~
foo.cc:27:6: note: template argument deduction/substitution failed:
foo.cc:39:32: note: couldn't deduce template parameter 'template<class> class A'
bool b = (t.begin() == t.end());
^
What are some correct ways to do this? Is it even possible when template templates are involved?
The simpler way is to create it inside the struct directly (as member or friend function):
template <template<class> class A, template<class> class B>
struct basic_thing {
// ...
struct iterator {
bool equal (const iterator& other) { return true; }
bool operator ==(const iterator& rhs) const;
// friend bool operator ==(const iterator& lhs, const iterator& rhs);
};
};
With
template <template<class> class A, template<class> class B>
bool operator== (const typename basic_thing<A, B>::iterator& lhs,
const typename basic_thing<A, B>::iterator& rhs);
A and B are not deducible (on the left of ::).
so only callable the ugly way:
bool b = operator==<detail::foo, detail::bar>(t.begin(), t.begin());
Related
I'm developing a header-only library for automatic/algorithmic differentiation. The goal is to be able to simply change the type of the variables being fed to a function and calculate first and second derivatives. For this, I've created a template class that allows the programmer to select the storage type for the private data members. Included is a snippet below with an offending operator overload.
template <typename storage_t>
class HyperDual
{
template <typename T> friend class HyperDual;
public:
template <typename T>
HyperDual<storage_t> operator+(const HyperDual<T>& rhs) const
{
HyperDual<storage_t> sum;
for (size_t i = 0; i < this->values.size(); i++)
sum.values[i] = this->values[i] + rhs.values[i];
return sum;
}
protected:
std::vector<storage_t> values;
};
Later on, to maximize the versatility, I provide template functions to allow interaction.
template <typename storage_t, typename T>
HyperDual<storage_t> operator+(const HyperDual<storage_t>& lhs, const T& rhs)
{
static_assert(std::is_arithmetic<T>::value && !(std::is_same<T, char>::value), "RHS must be numeric");
return HyperDual<storage_t>(lhs.values[0] + rhs);
}
template <typename storage_t, typename T>
HyperDual<storage_t> operator+(const T& lhs, const HyperDual<storage_t>& rhs)
{
static_assert(std::is_arithmetic<T>::value && !(std::is_same<T, char>::value), "LHS must be numeric");
return HyperDual<storage_t>(lhs + rhs.values[0]);
}
What I'm encountering is that the compiler is trying to instantiate the second non-member template function.
#include "hyperspace.h"
int main()
{
HyperDual<long double> one(1); // There is an appropriate constructor
HyperDual<double> two(2);
one + two;
return 0;
}
I get the static_assert generated error "LHS must be numeric" for this. How would I resolve the ambiguity?
use enable_if_t to make the non-member template can only be applied in the specific context?
template <typename storage_t, typename T, typename = enable_if_t<std::is_arithmetic<T>::value && !(std::is_same<T, char>::value)>>
HyperDual<storage_t> operator+(const HyperDual<storage_t>& lhs, const T& rhs)
{
static_assert(std::is_arithmetic<T>::value && !(std::is_same<T, char>::value), "RHS must be numeric");
return HyperDual<storage_t>(lhs.values[0] + rhs);
}
the static_assert may be duplicated here.
Ok. I found my own issue. It comes down to the difference between static_assert and std::enable_if
Replacing my template declaration and removing static_assert, I achieve equivalent functionality:
template <typename storage_t, typename T,
typename = typename std::enable_if<std::is_arithmetic<T>::value && !std::is_same<T, char>::value>::type>
HyperDual<storage_t> operator+(const T& lhs, const HyperDual<storage_t>& rhs)
{
return HyperDual<storage_t>(lhs + rhs.value());
}
(Small detail, but rhs.values[0] was replaced with rhs.value(). This had nothing to do with the template issue, but was related to member access.
Given the following code:
template<class T>
class TemplateClass {
T val;
public:
TemplateClass(T val) :
val(val) {
}
TemplateClass(const TemplateClass& tc) = default;
TemplateClass& operator=(const TemplateClass& tc) = default;
class Ele {
T x;
public:
Ele(T x) :
x(x) {
}
template<class S>
friend bool operator==(const typename TemplateClass<S>::Ele& e1,
const typename TemplateClass<S>::Ele& e2);
};
};
template<class T>
bool operator==(const typename TemplateClass<T>::Ele& e1,
const typename TemplateClass<T>::Ele& e2) {
return e1.x == e2.x;
}
int main() {
TemplateClass<int>::Ele e1(4);
TemplateClass<int>::Ele e2(3);
if (e1 == e2) { // *********** error
std::cout << "ok" << std::endl;
}
}
I get the following error:
no match for 'operator==' (operand types are 'TemplateClass<int>::Ele' and 'TemplateClass<int>::Ele')
Can someone tell me how can I fix it and what is the problem?
Nested name of a type belongs to non-deduced contexts:
1) The nested-name-specifier (everything to the left of the scope resolution operator ::) of a type that was specified using a qualified-id:
So operator== fails to be invoked because template argument deduction fails; template parameter can't be deduced.
You might make it a non-template function, and define it inside the class definition. e.g.
template<class T>
class TemplateClass {
...
class Ele {
...
friend bool operator==(const typename TemplateClass<T>::Ele& e1,
const typename TemplateClass<T>::Ele& e2) {
return e1.x == e2.x;
}
};
};
LIVE
Or make it non-template member function, then you might define them out of the class definition. e.g.
template<class T>
class TemplateClass {
...
class Ele {
...
bool operator==(const typename TemplateClass<T>::Ele& e2) const;
bool operator!=(const typename TemplateClass<T>::Ele& e2) const;
};
};
template<class T>
bool TemplateClass<T>::Ele::operator==(const typename TemplateClass<T>::Ele& e2) const {
return x == e2.x;
}
template<class T>
bool TemplateClass<T>::Ele::operator!=(const typename TemplateClass<T>::Ele& e2) const {
return ! (*this == e2);
}
LIVE
In addition to the answers here, you can use friend injection technique to fix the error:
template<class T>
class TemplateClass {
// ...
class Ele {
// ...
friend bool operator==(const Ele& e1, const Ele& e2) {
return e1.x == e2.x;
}
};
};
Note that you do not need problematic typename TemplateClass<S>::Ele here at all.
I am trying to implement an overload for operator!= that compares two objects of different types (InnerA and InnerB). Both of the types are defined as nested classes within a template class (Outer). The overload needs to be a friend of both classes as it accesses private fields from each.
template<typename Type> class Outer
{
public:
class InnerA;
class InnerB;
};
template<typename Type> bool operator!=(const typename Outer<Type>::InnerA& lhs, const typename Outer<Type>::InnerB& rhs);
template<typename Type> class Outer<Type>::InnerA
{
const int val = 0;
friend bool operator!=<>(const InnerA& lhs, const typename Outer<Type>::InnerB& rhs);
};
template<typename Type> class Outer<Type>::InnerB
{
const int val = 1;
friend bool operator!=<>(const typename Outer<Type>::InnerA& lhs, const InnerB& rhs);
};
template<typename Type> bool operator!=(const typename Outer<Type>::InnerA& lhs, const typename Outer<Type>::InnerB& rhs)
{
return lhs.val != rhs.val;
}
int main()
{
bool b = Outer<int>::InnerA() != Outer<int>::InnerB();
}
The above code fails to compile, emitting:
In instantiation of 'class Outer<int>::InnerA':
34:33: required from here
15:17: error: template-id 'operator!=<>' for 'bool operator!=(const Outer<int>::InnerA&, const Outer<int>::InnerB&)' does not match any template declaration
In instantiation of 'class Outer<int>::InnerB':
34:57: required from here
22:17: error: template-id 'operator!=<>' for 'bool operator!=(const Outer<int>::InnerA&, const Outer<int>::InnerB&)' does not match any template declaration
In function 'int main()':
34:35: error: no match for 'operator!=' (operand types are 'Outer<int>::InnerA' and 'Outer<int>::InnerB')
34:35: note: candidate is:
26:30: note: template<class Type> bool operator!=(const typename Outer<Type>::InnerA&, const typename Outer<Type>::InnerB&)
26:30: note: template argument deduction/substitution failed:
34:57: note: couldn't deduce template parameter 'Type'
While there might be better ways to achieve a similar result, I'm curious as to what precisely is wrong with my code. Thanks!
template<typename Type> class Outer<Type>::InnerA
{
friend bool operator!=(const InnerA& lhs, const typename Outer<Type>::InnerB& rhs) { return true; }
};
template<typename Type> class Outer<Type>::InnerB
{
friend bool operator!=(const typename Outer<Type>::InnerA& lhs, const InnerB& rhs) { return true; }
};
these are non-template friends. They also conflict. So implement one of them, omit the other.
They will be found via ADL.
I found the following workaround, refactoring the overload as a method of one of the nested classes:
template<typename Type> class Outer
{
public:
class InnerA;
class InnerB;
};
template<typename Type> class Outer<Type>::InnerA
{
const int val = 0;
public:
bool operator!=(const typename Outer<Type>::InnerB& other);
};
template<typename Type> class Outer<Type>::InnerB
{
const int val = 1;
friend bool Outer<Type>::InnerA::operator!=(const InnerB& other);
};
template<typename Type> bool Outer<Type>::InnerA::operator!=(const typename Outer<Type>::InnerB& other)
{
return val != other.val;
}
int main()
{
bool b = Outer<void>::InnerA() != Outer<void>::InnerB();
}
However, I am still curious if the same can be accomplished using a non-member friend function as in the question.
The issue with the code in the question ended up being that template deduction is not attempted on type names nested inside a dependent type (e.g. Outer<Type>::Inner).
This question is essentially a duplicate of Nested template and parameter deducing. A detailed explanation on why this is a problem can be found here.
I am trying to do matrix addition using expression templates and for this task I have base class: Exp
template<typename subtype>
class Exp{
public:
inline const subtype& self(void) const{
return *static_cast<const subtype*>(this);
}
};
a derived class: matrix
template<typename T,unsigned rows_,unsigned cols_ >
class matrix : public Exp<matrix<T,rows_,cols_>>{
//some members
};
and another derived class: addExp
template<typename T, typename Op1 , typename Op2>
class addExp: public Exp< addExp<T,Op1,Op2> >{
const Op1& op1;
const Op2& op2;
public:
addExp(const Op1& a, const Op2& b): op1(a), op2(b){}
T& operator()(const std::size_t i,const std::size_t j) const{
return op1(i,j) + op2(i,j);
}
};
I am now trying to do operator overloading on addExp for adding matrices.
template<typename T,typename Lhs, typename Rhs>
inline addExp<T,Lhs, Rhs>
operator+(const Exp<Lhs> &lhs, const Exp<Rhs> &rhs) {
return addExp<T,Lhs, Rhs>(lhs.self(), rhs.self());
}
later in my code I try to put two matrix objects(which should have Exp as base class) as function parameters here but I get an error:
prog.cpp: In function ‘int main()’:
prog.cpp:76:25: error: no match for ‘operator+’ (operand types are ‘matrix<int, 3u, 3u>’ and ‘matrix<int, 3u, 3u>’)
matrix<int,3,3> m3 = m1+m2;
~~^~~
prog.cpp:69:1: note: candidate: template<class T, class Lhs, class Rhs> addExp<T, Lhs, Rhs> operator+(const Exp<Lhs>&, const Exp<Rhs>&)
operator+(const Exp<Lhs> &lhs, const Exp<Rhs> &rhs) {
^~~~~~~~
prog.cpp:69:1: note: template argument deduction/substitution failed:
prog.cpp:76:26: note: couldn't deduce template parameter ‘T’
matrix<int,3,3> m3 = m1+m2;
where did I go wrong here and how do I fix this?
You need to somehow transfer the information of what T is to yout operator+. It can be done in several ways, and what way is best would heavily depends on your use case.
Here is one way of doing it.
template<typename T,unsigned rows_,unsigned cols_ >
class matrix : public Exp<matrix<T,rows_,cols_>>{
using type = T;
//some members
};
template<typename Lhs, typename Rhs, typename T = typename Lhs::type>
inline addExp<T,Lhs, Rhs>
operator+(const Exp<Lhs> &lhs, const Exp<Rhs> &rhs) {
return addExp<T,Lhs, Rhs>(lhs.self(), rhs.self());
}
I'm trying to specialize a member function on a templated class by a type trait of it's template parameter, but my forward declaration is apparently incorrect. Is there an easy fix?
#include <type_traits>
template <typename T>
class TTest{
public:
T data;
// edited to comment this out, template<typename U>
bool operator !=(const TTest& other) const;
};
template <typename T>
typename std::enable_if<std::is_unsigned<T>::value, bool>::type
TTest<T>::operator !=(const TTest<T>& other) const{
return true;
}
template<typename T>
bool TTest<T>::operator !=(const TTest<T>& other) const{
return false;
}
int main(){
TTest<size_t> t1;
TTest<int> t2;
}
Clang tells me:
templateTest.cpp:13:11: error: out-of-line definition of 'TTest::operator!='
differs from the declaration in the return type
TTest<T>::operator !=(const TTest<T>& other) const{
^
templateTest.cpp:8:8: note: previous declaration is here
bool operator !=(const TTest& other) const;
^
1 error generated.
It seems like the whole enable_if shebang is part of the function signature (or I don't really understand the errors). I can get the code to compile and behave as you want if I change it to
template <typename T>
class TTest{
public:
T data;
template<typename U>
typename std::enable_if<std::is_unsigned<U>::value, bool>::type
operator !=(const TTest<U>& other) const;
template<typename U>
typename std::enable_if<not std::is_unsigned<U>::value, bool>::type
operator !=(const TTest<U>& other) const;
};
template <typename T>
template <typename U>
typename std::enable_if<std::is_unsigned<U>::value, bool>::type
TTest<T>::operator !=(const TTest<U>&) const{
return true;
}
template <typename T>
template <typename U>
typename std::enable_if<not std::is_unsigned<U>::value, bool>::type
TTest<T>::operator !=(const TTest<U>&) const{
return false;
}
Live demo. Of course, this gets a lot less repetitive if you define those operators inline.
A better approach might be to dispatch to different private implementations of the operator logic based on the traits of T. This removes all the SFINAE verbosity from your code.
template <typename T>
class TTest{
public:
T data;
bool operator!=(const TTest& other) const
{
return not_equal_to(other, typename std::is_unsigned<T>::type());
}
private:
bool not_equal_to(TTest const&, std::true_type) const
{
return true;
}
bool not_equal_to(TTest const&, std::false_type) const
{
return false;
}
};
Live demo
You declared a member function template of you class template and you are trying to specialize it as a member function. That's not going to fly. Also, the return types differ although end up evaluating to the same thing.
I don't know what you are trying to attempt as the type U of the member function template isn't even deduced (did you mean the argument to be of type TTest<U>?). If you want to specialize your member based on a trait, I think you'll either need t overload the operator or use a different approach (e.g., delegating the implementation to a specialized class template):
#include <iostream>
#include <type_traits>
template <typename T>
class TTest{
public:
T data;
template<typename U>
typename std::enable_if<!std::is_unsigned<U>::value, bool>::type
operator !=(const TTest<U>& other) const;
template<typename U>
typename std::enable_if<std::is_unsigned<U>::value, bool>::type
operator !=(const TTest<U>& other) const;
};
template <typename T>
template<typename U>
typename std::enable_if<!std::is_unsigned<U>::value, bool>::type
TTest<T>::operator !=(const TTest<U>& other) const {
return true;
}
template <typename T>
template<typename U>
typename std::enable_if<std::is_unsigned<U>::value, bool>::type
TTest<T>::operator !=(const TTest<U>& other) const {
return false;
}
int main(){
TTest<unsigned int> t1;
TTest<int> t2;
std::cout << std::boolalpha
<< "t1 != t1: " << (t1 != t1) << '\n'
<< "t1 != t2: " << (t1 != t2) << '\n'
<< "t2 != t1: " << (t2 != t1) << '\n'
<< "t2 != t2: " << (t2 != t2) << '\n';
}
Tag dispatching is the clean way to do this:
template <typename T>
class TTest{
bool not_equal( const ITest& other, std::true_type /* is unsigned */ ) const;
bool not_equal( const ITest& other, std::false_type /* is unsigned */ ) const;
public:
T data;
bool operator !=(const TTest& other) const {
return not_equal( other, std::is_unsigned<T>() );
}
};
now simply implement the two TTest<T>::not_equal overloads. Only the one that is actually called for a given T will be compiled past basic parsing.