Explicit specialization template member and typedef - c++

Found a curious emergent architecture (it wasn't designed intentionally but was created over time by adding up features) in code base, which looks like this, if dumbed down:
#include <iostream>
// Base class
template <class T>
struct Base {
typedef T DataT;
void foo();
};
// Parameter classes
struct A { int a[2]; };
struct B { int a[3]; };
// In original code Base<> had a complex list of arguments
typedef Base<A> ContainerA;
typedef Base<B> ContainerB;
// what allows to use ContainerA? It's a type name or explicit specialization?
// Class parameters, e.g. T, are unusable in this context directly.
template<> void ContainerA::foo() {
std::cout << "ContainerA " << sizeof(DataT) << std::endl;
}
template<> void ContainerB::foo() {
std::cout << "ContainerB " << sizeof(DataT) << std::endl;
}
int main()
{
ContainerA a;
ContainerB b;
a.foo();
b.foo();
}
Essentially, with multiple derived classes and complex template argument list this use of typedef saves time to type code and makes specialization declarations more readable. But which part of standard says that a typedef can be used in such way?
Code had partial support of C++11, no using = type declarations allowed.

Related

Inner template classes inheritance

I have the following code: https://gist.github.com/PatrikValkovic/50329975f86e0328ff1f85fda17a23f3, live example here: http://cpp.sh/675a3.
In short, I have class A with inner class B<U> and class D that should inherit from the B<U>. The code works when the class D is declared within class A (as commented). However, when I move the declaration outside of the class A (as in the example), the compiler complains, that A<T>::B<U> is not a type. What is wrong about the syntax?
// Example program
#include <iostream>
#include <string>
using namespace std;
template<typename T>
class A
{
public:
template<typename U>
class B;
/*
class D : public B<int>
{
public:
void method2() {
cout << "Method 2" << endl;
this->method1();
}
};
*/
class D;
};
template<typename T>
template<typename U>
class A<T>::B
{
public:
void method1() {
T x;
cout << "Method 1: " << x << endl;
}
};
template<typename T>
class A<T>::D : public A<T>::B<int>
{
public:
void method2() {
cout << "Method 2" << endl;
this->method1();
}
};
int main()
{
A<int>::D b;
b.method2();
}
This is one of those really weird edge cases in the C++ language that has a unusual fix. The issue is here:
template<typename T>
class A<T>::D : public A<T>::B<int>
^^^^^^^^^^^^
The problem is that you are trying to use the template class B, which is a dependent name inside of A (that is, B is a template nested inside another type that depends on a template argument, here, T). By default, C++ won't treat dependent names as being names of types or names of templates, and you have to explicitly tell the compiler "yes, this is the name of a template" by using the template keyword in an unusual way:
template<typename T>
class A<T>::D : public A<T>::template B<int>
^^^^^^^^
This tells C++ "inside of A<T>, you're going to find a template type named B. Please use that template with argument int."
This is similar to how you have to use the typename keyword with dependent types - it tells the compiler "yes, this is the name of a type." Here, the template keyword tells the compiler "yes, this is the name of a template."

Zero-sized member variable in templated struct

I have a templated struct that has a variant with an extra float, like this:
template <bool HasFloat>
struct Foo {
std::vector<int> a;
float b; // Not needed if HasFloat is false
};
To save memory (yes it is significant) I'd like to omit that float if HasFloat is false. Since there is a lot of other stuff in the struct the best way would be something like this:
using B = typename std::conditional<HasFloat, float, ZeroSizedType>::type;
B b;
Except there is no such thing as a zero sized type in C++ as far as I can tell. The only exception seems to be "Flexible Array Members", so I could maybe do something like this:
using B = typename std::conditional<HasFloat, float, float[]>::type;
Except they are only supported in C99, not C++.
The standard solution to this seems to be to use inheritance, since base classes can be zero-sized, however my struct is also accessed by assembly and to make the assembly simpler it is better if the float b; is at the end of the struct rather than the beginning, and anyway that isn't guaranteed.
So this seems to leave template specialisation as the only option but my class is actually rather long and I'd like to avoid duplicating everything. Is there another solution that I'm missing?
One of my colleagues came up with a fairly nice solution. It does require copy & pasting the data members but at least I don't have to duplicate my methods.
template <bool HasFloat>
struct Foo {
struct State {
std::vector<int> a;
};
struct StateWithFloat {
std::vector<int> a;
float k;
};
using FooState = std::conditional_t<HasFloat, StateWithFloat, State>;
FooState state;
};
You could do:
struct StateWithFloat {
State state;
float k;
};
But then you have to add template functions to avoid the state.a vs state.state.a problem and copy & pasting seems easier at that point.
Also #aschepler pointed out that in C++20 you will be able to use [[no_unique_address]].
You can create a struct specializing for has float or not, and then use it in your struct forwarding the template parameter. Something like this:
template <bool HasFloat>
struct PerhapsFloat;
template <>
struct PerhapsFloat<true>
{
float b;
};
template <>
struct PerhapsFloat<false>
{
};
template <bool HasFloat>
struct Foo {
std::vector<int> a;
PerhapsFloat<HasFloat> b;
};
Here, you have a demo:
https://godbolt.org/z/7zPto9
Try to extract the specialization to a base/member class.
template <bool B>
struct Foo {};
template <>
struct Foo<true>
{
float f;
};
template <bool HasFloat>
class Bar
{
Foo<HasFloat> b;
};
class Empty {};
int main()
{
std::cout << sizeof(Bar<true>) << std::endl; // 4
std::cout << sizeof(Bar<false>) << std::endl; // 1
std::cout << sizeof(Empty) << std::endl; // 1
}

set public/private on template function for some certain template parameter

Is it possible to make a certain template function have 2 accessibility level for some certain template parameter? (via splitting into 2 functions?)
class B{
enum ENU{
T0,T1,T2
}
template<ENU T=T0> someType f(){ ... } //want T1,T2 = public, T0 = private
};
Current usage (the solution should not change it):-
B b;
int aa=b.f<T0>(); //should fail
std::string bb=b.f<T1>();// should ok
Edit: B has a lot of functions like this.
Here is the full code (just in case someone want to edit or use) https://ideone.com/ryNCml.
I doubt what you are trying to do is possible, as specializing functions on a value ain't allowed in C++.
Though if you don't need enumerations, you could write something similar:
class B {
public:
struct T0{};
struct T1{};
struct T2{};
template<typename T> void f(T, ...) {
static_assert(std::is_same_v<T, T1> || std::is_same_v<T, T2>);
}
private:
void f(T0, ...) {}
};
int main(int argc, char **argv) {
B b{};
b.f(T1{}); // Should compile
b.f(T0{}); // Should not compile
}
If you would use the same function implementation, you can either forward this to a common method, or simply put T0 private.
Alternatively, you could make use of a proxy object which could convert the value, though I'm not sure if this is standard C++ of an extension of the compiler I'm familiar with:
class B {
public:
enum class T { //< Strong typed!
T0,
T1,
T2
}
template <T t>
struct TWrapper {};
template <T ActualT>
void f(..., TWrapper<ActualT> tw = TWrapper<ActualT>{});
private:
template <>
struct TWrapper<T0> {};
}
As far as I can understand, you want to forbid the use of T0 as a template parameter while invoking member method f.
To do that, you can use either std::enable_if or a static_assert.
It follows a minimal, working example:
#include<type_traits>
class B {
public:
enum ENU { T0,T1,T2 };
template<ENU T>
std::enable_if_t<(T==T1||T==T2),int>
f() { return 42; }
template<ENU T>
int g(){
static_assert(T==T1||T==T2, "not allowed");
return 42;
}
};
int main() {
B b;
b.f<B::T1>();
// It doesn't work
//b.f<B::T0>();
b.g<B::T1>();
// It doesn't work
//b.g<B::T0>();
}
Given you want to support only a finite set of template arguments, I would write three functions which are not template functions, and give them the right visibility. Then make them delegate to a private template function which does the work. This would look like:
class B{
public:
enum ENU{
T0,T1,T2
}
private:
template<ENU T=T0> int f(){
std::cout<<"In enum "<<T<<std::endl;
return 0;
}
protected:
someType fT0() { return f<T0>(); }
public:
someType fT1() { return f<T1>(); }
someType fT2() { return f<T2>(); }
};
Contrary to your requirements, the usage changes - but this is often the simplest approach:
B b;
int aa=b.fT0(); // fails
int bb=b.fT1();// ok
Alternatively, you can make the template be public, but give it a dummy argument (with a default), and make the type of the dummy argument depend on the template parameter (via traits). If the type of the dummy is a private class, the template will only be callable by a member.
template <ENU T>
struct protection_traits;
class B{
friend class protection_traits<T0>; // So it has access to Protected.
protected:
struct Protected{};
public:
struct Public{};
enum ENU{
T0,T1,T2
}
template<ENU T=T0> int f( typename protection_traits<T>::type = {})
{ std::cout<<"In enum "<<T<<std::endl; }
};
template <ENU T>
struct protection_traits
{
typedef B::Public type; // Default to public
};
template<>
struct protection_traits<T0>
{
typedef B::Protected type; // But T0 uses Protected
};
Usage:
B b;
int aa=b.f<T0>(); // fails (no access to B::Protected)
int bb=b.f<T1>(); // ok
Note: This latter solution hasn't been fed to a compiler. There will be typos.

Specializations only for C++ template function with enum non-type template parameter

This question is related to this one except that rather than dealing with typename template parameters, I am trying to use an enum non-type template parameter.
Is it possible to have a templated (class member function) with only specializations, no general (working) definition in the case of non-type template parameter?
I was able to get one version working, by declaration in the class body and providing specializations only, but any misuse calling with a non-defined template parameter doesn't produce an error until linking. What's worse is the missing symbol cryptically refers to the enum's integral value and not its name, so it would be confusing to other developers.
I was able to get the BOOST_STATIC_ASSERT technique from the referenced question to work for typename template parameter only.
This code demonstrates the idea. I don't want the CAT-version call to compile:
#include <iostream>
#include <boost/static_assert.hpp>
// CLASS HEADER FILE:
struct foo_class
{
enum AllowedTypes { DOG, CAT };
template <AllowedTypes type>
void add_one_third( double bar ) const
{
BOOST_STATIC_ASSERT_MSG(sizeof(type)==0, "enum type not supported.");
}
};
// CLASS SOURCE FILE
template<>
void foo_class::add_one_third<foo_class::DOG>( double bar ) const
{
std::cout << "DOG specialization: " << bar + 1./3. << std::endl;
}
// USER SOURCE FILE
int main()
{
std::cout << "Template Specialization!\n\n";
foo_class a;
a.add_one_third<foo_class::DOG>(3.0); // should succeed
// Compilation fails with or without the following line:
a.add_one_third<foo_class::CAT>(3.0); // should fail at compile-time
return 0;
}
Background:
I have a class member function that takes an enum "ArgType" and a name.
void declareKernelArgument( ArgType type, std::string name );
The definition has turned into an if..else..if..else list for the half-dozen or so allowed ArgType cases. I also have to have final case that throws an exception for an not-allowed ArgType. I'm thinking it would be cleaner to move ArgType to a template parameter, and provide a specialization for each allowed ArgType. Misuse would be caught at compile-time.
With partial specialization of a structure inside the class:
#include <iostream>
class foo_class
{
public:
enum AllowedTypes { T_DOUBLE, T_INT };
private:
template <AllowedTypes type, typename T>
struct AddOneThird;
template <typename T>
struct AddOneThird<T_DOUBLE, T> {
static void apply(T bar) {
std::cout << "T_DOUBLE specialization: " << bar + 1.0/3.0 << std::endl;
}
};
public:
template <AllowedTypes type>
void add_one_third( double bar ) const {
AddOneThird<type, double>::apply(bar);
}
};
int main() {
foo_class a;
a.add_one_third<foo_class::T_DOUBLE>(3.0);
// error: incomplete type ‘foo_class::AddOneThird<(foo_class::AllowedTypes)1u
// a.add_one_third<foo_class::T_INT>(3.0); // should fail at compile-time
return 0;
}
With full specialization of a (friend) class:
#include <iostream>
class foo_class
{
public:
enum AllowedTypes { T_DOUBLE, T_INT };
// if needed
// template<AllowedTypes> friend struct AddOneThird;
public:
template <AllowedTypes type> void add_one_third( double bar ) const;
};
template <foo_class::AllowedTypes>
struct AddOneThird;
template <>
struct AddOneThird<foo_class::T_DOUBLE> {
static void apply(double bar) {
std::cout << "T_DOUBLE specialization: " << bar + 1.0/3.0 << std::endl;
}
};
template <foo_class::AllowedTypes type>
void foo_class::add_one_third( double bar) const {
AddOneThird<type>::apply(bar);
}
int main() {
foo_class a;
a.add_one_third<foo_class::T_DOUBLE>(3.0);
// error: incomplete type ‘AddOneThird<(foo_class::AllowedTypes)1u>’ used
// in nested name specifier
//a.add_one_third<foo_class::T_INT>(3.0); // should fail at compile-time
return 0;
}
Utilizing C++11 or boost::enable_if:
#include <iostream>
#include <type_traits>
class foo_class
{
public:
enum AllowedTypes { T_DOUBLE, T_INT };
template <AllowedTypes type>
typename std::enable_if<type == T_DOUBLE>::type
add_one_third( double bar ) const {
std::cout << "T_DOUBLE specialization: " << bar + 1.0/3.0 << std::endl;
}
};
int main() {
foo_class a;
a.add_one_third<foo_class::T_DOUBLE>(3.0);
// error: no matching function for call to ‘foo_class::add_one_third(double)’
//a.add_one_third<foo_class::T_INT>(3.0); // should fail at compile-time
return 0;
}
From Herb Sutter
It's a lot less intuitive to specialize function templates. For one thing, you can't partially specialize them -- pretty much just because the language says you can't.[2] For another thing, function template specializations don't overload. This means that any specializations you write will not affect which template gets used, which runs counter to what most people would intuitively expect. After all, if you had written a nontemplate function with the identical signature instead of a function template specialization, the nontemplate function would always be selected because it's always considered to be a better match than a template.
If you're writing a function template, prefer to write it as a single function template that should never be specialized or overloaded, and implement the function template entirely in terms of a class template. This is the proverbial level of indirection that steers you well clear of the limitations and dark corners of function templates. This way, programmers using your template will be able to partially specialize and explicitly specialize the class template to their heart's content without affecting the expected operation of the function template. This avoids both the limitation that function templates can't be partially specialized, and the sometimes surprising effect that function template specializations don't overload. Problem solved.
Your enum type sizeof is not 0, change that to 4 at least. Otherwise this will not work. A enum element size is not 0.
Without that everything runs
#include <iostream>
struct foo_class
{
enum AllowedTypes { DOG, CAT };
template <AllowedTypes type>
void add_one_third( double bar ) const
{
std::cout << "YES" << std::endl;
}
};
template<>
void foo_class::add_one_third<foo_class::DOG>( double bar ) const
{
std::cout << "DOG specialization: " << bar + 1./3. << std::endl;
}
int main()
{
std::cout << "Template Specialization!\n\n";
foo_class a;
a.add_one_third<foo_class::DOG>(3.0); // should succeed
// Compilation fails with or without the following line:
//a.add_one_third<foo_class::CAT>(3.0); // should fail at compile-time
return 0;
}
The main difference between the enum case and the referenced question using a typename parameter is that the default definition will be compiled for any use. So, a working solution is as simple as modifying the BOOST_STATIC_ASSERT condition to check allowed enum values.
#include <iostream>
#include <stdexcept>
#include <boost/static_assert.hpp>
// CLASS HEADER FILE:
struct foo_class
{
enum AllowedTypes { DOG, CAT, MOUSE };
template <AllowedTypes type>
void give_bath() const
{
// compile fails if ever attempting to use this function with CAT parameter.
BOOST_STATIC_ASSERT_MSG( (type==DOG) || (type==MOUSE) , "enum type not supported.");
throw std::runtime_error("Unexpected. Above list inconsistent with specializations.");
}
};
// CLASS SOURCE FILE
template<>
void foo_class::give_bath<foo_class::DOG>() const
{
std::cout << "DOG is bathed." << std::endl;
}
template<>
void foo_class::give_bath<foo_class::MOUSE>() const
{
std::cout << "MOUSE is bathed." << std::endl;
}
// USER SOURCE FILE
int main()
{
std::cout << "Template Specialization!\n\n";
foo_class a;
a.give_bath<foo_class::DOG>(); //success
a.give_bath<foo_class::MOUSE>(); // success
// Compilation fails with the following line:
//a.give_bath<foo_class::CAT>(); // fails at compile-time as intended.
return 0;
}
Of course, the whole design smells bad and could likely be handled more elegantly with AllowedTypes being a struct/class with inherited specializations. But this gets to the question at hand.

Ignored traits specialization in c++

I have some hard time implementing traits in C++, I tried to follow several examples from the internet but it still doesn't want to compile.
I use a Term class, which contains an Attribute, an Operator and sometimes a value. For example, age < 10 or color == red are (simple) terms. Different kinds of attributes or operators exists, that inherits from classes Attribute or TermOperator.
Since a lot of methods of the term class will depend on the attribute and the operator, this is a template class. In order to simplify the manipulation of terms, I added an abstract class : AbstractTerm
class AbstractTerm {
protected:
Attribute* pAttribute;
TermOperator* pOperator;
public:
virtual bool eval(Data *) const = 0;
};
template <typename ATT, typename OP>
class Term : AbstractTerm {
typedef typename TermTraits<ATT,OP>::type VALUE_TYPE;
private:
typename TermTraits<ATT,OP>::type value;
public:
bool eval(Data *) const;
};
The value I need to store in term will depend on both attribute & operator, so I use traits to obtain the right type to store the value.
template < typename ATT, typename OP>
struct TermTraits
{
typedef int type;
};
template <>
struct TermTraits<ListAttribute, TermOperator>
{
typedef ListValue type;
};
template <>
struct TermTraits<NumericAttribute, TermOperator>
{
typedef NumericIntValue type;
};
However, in the eval method when I use VALUE_TYPE I don't get the right type
template <> bool Term<NumericAttribute, TermOperatorEquals>::eval(Data _data) const {
// VALUE_TYPE is a int, but it should be a NumericIntValue
VALUE_TYPE *pValue = data->getValue<VALUE_TYPE>(this->pAttribute->getId());
return !pValue->isEmpty && (this->value == pValue->value); // I get compilation errors here because pValue is a int* and not a 'NumericIntValue*'
};
I get the error:
error: request for member 'isEmpty' in '* pValue',
which is of non-class type 'Term<NumericAttribute,
TermOperatorExists>::VALUE_TYPE {aka int}.
I can't figure out why it doesn't use the specialisation TermTraits<NumericAttribute, TermOperator>, since TermOperatorExists inherits from TermOperator.
Traits are a new concept to me, so maybe I made some obvious mistakes. If someone has a better way or simpler way to do this I'm also interested.
Although TermOperatorExists inherits from TermOperator, those are different types, so template specialization is not called for TermOperatorExists. You need to explicitly cast TermOperatorExists to its base class in order to get specialization called.
Example:
#include <iostream>
using namespace std;
class base
{
};
class derrived: public base
{
};
class test
{
public:
template <class T> void print(T arg)
{
std::cout << "test" << std::endl;
}
};
template <>
void test::print<base>(base arg)
{
std::cout << "base specialiation" << std::endl;
};
int main()
{
cout << "Hello World" << endl;
base b;
derrived d;
test t;
t.print<int>(1);
t.print(b);
t.print(d);
t.print(static_cast<base>(d));
return 0;
}
outputs:
Hello World
test
base specialiation
test
base specialiation