I'm working on a C++11 wrapper around a C api. The C api offers a bunch of getters for various types, with a different name for each type. Values are retrieved by array of a given size, known at compilation.
I want to give the type and the array size by template, to call the right function.
#include <string>
#include <iostream>
template <typename T>
struct make_stop {
constexpr static bool value = false;
};
class Foo
{
public:
Foo() : i(42) {}
template<typename T, size_t n>
T get();
private:
int i = 0;
};
template<typename T, size_t n>
T Foo::get() { static_assert(make_stop<T>::value); return T(); }
template<int, size_t n>
int Foo::get() { return i + n; }
int main() {
Foo foo;
int i = foo.get<int, 4>();
double f = foo.get<double, 2>();
return 0;
}
But it fails to match the right function
main.cpp:26:5: error: no declaration matches 'int Foo::get()'
int Foo::get() { return i + n; }
^~~
main.cpp:15:7: note: candidate is: 'template<class T, long unsigned int n> T Foo::get()'
T get();
its a bit vauge from your question, but assuming you are wanting to index into some c- arrays and return the value at I you can't specialize function templates like you want, but you can use some tags instead, something like..
class Foo
{
public:
Foo() : is{1,2,3,4,5,6,7,8,9,10},ds{1.1,2.2,3.3,4.4,5.5,6.6,7.7,8.8,9.9,10.1} {}
template <typename T> struct type_c{};
template <size_t I> struct int_c{};
template<typename T,size_t I>
auto get()
{ return get_impl(type_c<T>(),int_c<I>()); }
private:
template <size_t I>
auto get_impl(type_c<int>,int_c<I>)
{ return is[I]; }
template <size_t I>
auto get_impl(type_c<double>,int_c<I>)
{ return ds[I]; }
int is[10];
double ds[10];
};
int main() {
Foo foo;
int i = foo.get<int,0>();
double d = foo.get<double,2>();
std::cout << i << " " << d << std::endl;
return 0;
}
Demo
If I understood you correctly you want to partially specialize get for T. Unfortunately partial specialization for methods is not allowed by the standard. You can however get around this with a static method on a class templated by T and specializing the class.
Like this:
template <class T> struct Foo_helper;
struct Foo
{
Foo() : i{42} {}
template<class T, std::size_t N>
T get()
{
return Foo_helper<T>::template get<N>(*this);
}
int i = 0;
};
template <class T> struct Foo_helper {};
// specialize Foo_helper for each type T you wish to support:
template <> struct Foo_helper<int>
{
template <std::size_t N>
static int get(const Foo& foo) { return foo.i + N; }
};
template <> struct Foo_helper<double>
{
template <std::size_t N>
static double get(const Foo& foo) { return foo.i + N; }
};
int main()
{
Foo foo{};
int i = foo.get<int, 4>();
double d = foo.get<double, 2>();
}
Related
I would like to define a class template (hereafter called C) which takes a reference to an object of an to-be instantiated class template (hereafter called S) as template parameter. The objective is that C can be fully instantiated with one template argument.
S is a class template on its own which has one integral type template parameter. The C class template shall be instantiated using a reference to an object of any instantiation of S.
This is what I am trying to achieve:
template<int I> struct S {
int get() { return 42 + I; }
};
// ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ my desperate attempt
template< typename S<int I>::template & n>
struct C {
int get() {
return n.get();
}
};
S<42> s;
int main()
{
C<s> c;
return c.get();
}
The compiler I am using supports GNU++11 or older.
In C++17, you might do
template<int I> struct S { int get() { return 42 + I; } };
template <auto& n>
struct C;
template <int I, S<I>& n>
struct C<n>
{
int get() { return n.get(); }
};
S<42> s;
int main()
{
C<s> c;
return c.get();
}
Demo.
Before C++17, template <auto& n> struct C; has to be replaced. for example by
template <typename T, T& n> struct C;
template <typename T, T& n>
struct C;
template <int I, S<I>& n>
struct C<S<I>, n>
{
int get() { return n.get(); }
};
S<42> s;
#define AUTO(x) decltype(x), x
int main()
{
C<S<42>, s> c;
// C<AUTO(s)> c;
return c.get();
}
Demo
There is no way that I know in C++11 that will allow you to change just the template parameters to do what you want. What you can do though is not have a non-type template parameter, but just a type, and add a constructor to C that takes the reference to the desired object as a parameter:
template<typename T>
struct C {
C(T &t): t(t) {}
int get() {
return t.get();
}
private:
T &t;
};
Then you could declare c as follows:
C<decltype(s)> c(s);
However, it is of course not so nice to have to repeat yourself like that, so the trick is to make a templated function that will construct a C of the right type for you:
template<typename T>
C<T> make_C(T &t) {
return C<T>(t);
}
And then you can write:
auto c = make_C(s);
This is not the answer. But maybe this helps somebody who stumbled upon this question or even help somebody to actually find the answer.
In contrast to the original question I added the static member variable S.i.
template<int I> struct S {
static constexpr int i = I;
int get() { return 42 + I; }
};
template<int I, S<I>& n> struct C
{
int get() {
return n.get();
}
};
S<42> s;
int main()
{
C<s.i, s> c;
return c.get();
}
This is not the answer, because still two template arguments are required in order to instantiate the class template C.
This compiles with C++11.
Let's assume an input template parameter T may or may not have internal variable bar. I am trying to write a struct that returns the value of bar when we have it, and returns some constant when we don't. Here is my attempt:
struct A {
static constexpr unsgined int bar = 20;
hasBar = true;
};
struct B {
hasBar = false;
};
template <typename T, typename std::enable_if<T::hasBar, int>::type>
struct getBar {
static constexpr unsigned int bar = T::bar;
};
template <typename T, typename std::enable_if<!T::hasBar, int>::type>
struct getBar {
static constexpr unsigned int bar = 0;
};
int main() {
getBar<A>::bar; // Expect 20
getBar<B>::bar; //Expect 0
}
I cannot compile this code with C++14. The compiler complains that: "template non-type parameter has a different type".
Why we have such an error and how can I address it?
Class templates can't be overloaded (like function templates); You can use specialization instead. e.g.
template <typename T, typename = void>
struct getBar {
static constexpr unsigned int bar = 0;
};
template <typename T>
struct getBar<T, std::enable_if_t<T::hasBar>> {
static constexpr unsigned int bar = T::bar;
};
LIVE
You can detect whether ::bar exists directly without the need for hasbar
something like ...
#include <type_traits>
#include <iostream>
struct A {
static constexpr unsigned int bar = 20;
};
struct B {
};
template <typename T,typename=void>
struct getBar {
static constexpr unsigned int bar = 0;
};
template <typename T>
struct getBar<T,std::void_t<decltype(T::bar)>> {
static constexpr unsigned int bar = T::bar;
};
int main() {
std::cout << getBar<A>::bar << std::endl; // Expect 20
std::cout << getBar<B>::bar << std::endl; //Expect 0
}
Demo
Another solution that doesn't needs hasBar but simply detect the presence of bar (and also maintain the original type of bar, if different from int)
struct A
{ static constexpr unsigned int bar = 20; };
struct B
{ };
template <typename T>
constexpr auto getBarHelper (int) -> decltype( T::bar )
{ return T::bar; }
template <typename T>
constexpr int getBarHelper (long)
{ return 0; }
template <typename T>
struct getBar
{ static constexpr auto bar { getBarHelper<T>(0) }; };
int main()
{
static_assert( 20u == getBar<A>::bar, "!" );
static_assert( 0 == getBar<B>::bar, "!" );
}
I have a the following class template
template<int N>
constexpr int arraySize() { return arraySize<N-1>() + N; }
template<>
constexpr int arraySize<0>() { return 0; }
template<int C>
class MyClass {
public:
std::array<int, arraySize<C>()> arr;
};
int main() {
MyClass<3> cls;
std::cout << cls.arr.size() << std::endl; // Output: 6
}
Everything works but I would like to have calculateArraySize<N>() as a member function. I've tried the following:
template<int C>
class MyClass {
public:
static constexpr int arraySize();
std::array<int, MyClass<C>::arraySize()> arr;
};
template<int C>
constexpr int MyClass<C>::arraySize(){ return MyClass<C-1>::arraySize() + C; }
template<>
constexpr int MyClass<0>::arraySize() { return 0; }
Results in the following error:
fatal error: recursive template instantiation exceeded maximum
depth of 1024
std::array::arraySize()> arr;
template<int C>
class MyClass {
public:
template<int N>
static constexpr int arraySize();
std::array<int, MyClass::arraySize<C>()> arr;
};
template<int C>
template<int N>
constexpr int MyClass<C>::arraySize(){ return MyClass::arraySize<N-1>() + N-1; }
template<int C>
template<>
constexpr int MyClass<C>::arraySize<0>() { return 0; }
Gives the following error:
tmp.cc:19:27: error: cannot specialize (with 'template<>') a member of an
unspecialized template
constexpr int MyClass::arraySize<0>() { return 0; }
Is it possible to achieve the desired behaviour? Solutions using C++14/C++17 features (I guess it should be possible usinn if-constexpr) are welcomed but won't solve my particular problem since only C++11 is available.
You can move the function into the class and the specialize the entire class for the base case. That looks like:
template<int C>
class MyClass {
public:
static constexpr int arraySize(){ return MyClass<C-1>::arraySize() + C; }
std::array<int, MyClass<C>::arraySize()> arr;
};
template<>
class MyClass<0> {
public:
static constexpr int arraySize(){ return 0; }
};
int main() {
MyClass<3> cls;
std::cout << cls.arr.size() << std::endl; // Output: 6
}
Live Example
You can also use a member variable instead of a member function.
template <int C>
class MyClass {
public:
static constexpr int array_size = MyClass<C-1>::array_size + C;
std::array<int, array_size> arr;
};
template <>
class MyClass<0> {
public:
static constexpr int array_size = 0;
};
I was reading this question and some other stuff : Are there cases where a typedef is absolutely necessary??
I wrote this code :
const int arrayOfInt[10] = {0};
template<typename T, int N> using X = const T (&)[N];
struct foo
{
template<typename T, int N> operator X<int,10> () { return arrayOfInt; }
};
void bar(const int (&) [10]) {}
int main()
{
bar(foo());
return 0;
}
using feature of c++11 is not working for me , also I'm unable to think how to typedef the return type in this case too since my class foo is not template itself. I need to see solution using using keyword and typedef both . Thanks a lot awesome peoples of SO :)
Since X is an alias template you need to provide the template arguments explicitly; they won't be captured from the surrounding scope:
struct foo
{
template<typename T, int N>
operator X<T,N>() { return arrayOfInt; }
// ^^^^^
};
You can't do this with a typedef as there's no such thing as a typedef template.
template<typename T, int N> operator X<int,10> () { return arrayOfInt; }
template arguments T and N are never used and hence shall never be deduced.
Fixed Live On Coliru
const int arrayOfInt[10]{0};
template<typename T, int N> using X = T const (&)[N];
struct foo {
template<typename T, int N> operator X<T,N> () const { return arrayOfInt; }
};
void bar(const int (&) [10]) {}
int main()
{
foo f;
X<int, 10> v = f;
bar(foo());
}
I have this simple code below, a template with 2 type parameters. If I declare my class with the same type (like BidirectionalMap<int,int>), I receive an error:
int BidirectionalMap<T,S>::operator [](T) const' : member function already defined or declared
Here's my template code:
template <class T, class S>
class BidirectionalMap{
int count(T t){
return 1;
}
int count(S s){
return 1;
}
};
The error you got is normal, because after substitution you have
template <>
class BidirectionalMap<int, int>
{
int count(int t){ return 1; }
int count(int s){ return 1; } // Duplicated method
};
To solve that, you may provide partial specialization:
template <class T>
class BidirectionalMap<T, T>
{
int count(T t) { return 1; }
};
In C++20, you might use requires to "discard" methods:
template <class T, class S>
class BidirectionalMap
{
int count(T t) requires(!std::is_same<T, S>::value) { /*..*/ }
int count(S s) requires(!std::is_same<T, S>::value) { /*..*/ }
int count(T t) requires( std::is_same<T, S>::value) { /*..*/ }
};