I have a problem where I want to specialize a template member function of a template class in the code below. The answer to this question explicit specialization of template class member function seems to suggest that it can't be done. Is that correct, and if so, is there any work around I can use so that by inline inc functions get expanded at compile time?
Many thanks!
#include <iostream>
#include <cstdio>
template <class IT, unsigned int N>
struct IdxIterator {
private:
int startIdx[N], endIdx[N];
int curIdx[N];
IT iter;
public:
IdxIterator(IT it, int cur[], int start[], int end[]): iter(it) {
for (int i = 0; i < N; i++) {
curIdx[i] = cur[i];
startIdx[i] = start[i];
endIdx[i] = end[i];
}
}
template <int dim>
inline void inc() {
curIdx[dim]++;
if (curIdx[dim] > endIdx[dim]) {
if (dim > 0) {
curIdx[dim] = startIdx[dim];
inc<dim-1>();
}
}
}
// how to declare this specialization?
template <> template <>
inline void inc<-1>() {
std::cerr << "IdxIterator::inc(" << -1 << ") dim out of bounds!\n";
throw 1;
}
inline IdxIterator<IT, N> operator++() {
iter++;
inc<N-1>();
return *this;
}
};
int main(int argc, char** argv) {
int *buf = new int[100];
int start[1], end[1];
start[0] = 0; end[0] = 99;
IdxIterator<int*, 1> it(buf, start, start, end);
++it;
return 0;
}
G++ spits out:
test2.cpp:32:13: error: explicit specialisation in non-namespace scope
‘struct IdxIterator’ test2.cpp:32:25: error: explicit
specialisation in non-namespace scope ‘struct IdxIterator’
test2.cpp:33:23: error: template-id ‘inc<-0x00000000000000001>’ in
declaration of primary template test2.cpp: In member function ‘void
IdxIterator::inc() [with int dim = -0x000000000000003fe, IT =
int*, unsigned int N = 1u]’: test2.cpp:27:9: error: template
instantiation depth exceeds maximum of 1024 (use -ftemplate-depth= to
increase the maximum) instantiating ‘void IdxIterator::inc()
[with int dim = -0x000000000000003ff, IT = int*, unsigned int N = 1u]’
test2.cpp:27:9: recursively instantiated from ‘void IdxIterator::inc() [with int dim = -0x00000000000000001, IT = int*, unsigned
int N = 1u]’ test2.cpp:27:9: instantiated from ‘void IdxIterator::inc() [with int dim = 0, IT = int*, unsigned int N = 1u]’
test2.cpp:41:5: instantiated from ‘IdxIterator
IdxIterator::operator++() [with IT = int*, unsigned int N =
1u]’ test2.cpp:53:5: instantiated from here
test2.cpp: At global scope: test2.cpp:22:15: warning: inline function
‘void IdxIterator::inc() [with int dim = -0x000000000000003ff,
IT = int*, unsigned int N = 1u]’ used but never defined [enabled by
default]
There may be a better way in C++11, but you can always go via overloading instead of specialization:
template <int N>
struct num { };
class A
{
template <int N>
void f(num <N>) { };
void f(num <-1>) { };
public:
template <int N>
void f() { f(num <N>()); };
};
You can do what the compiler error message suggests:
template <class IT, unsigned int N>
struct IdxIterator {
private:
template <int dim>
inline void inc() {
curIdx[dim]++;
if (curIdx[dim] > endIdx[dim]) {
if (dim > 0) {
curIdx[dim] = startIdx[dim];
inc<dim-1>();
}
}
}
};
template <> template <>
inline void IdxIterator::inc<-1>() {
std::cerr << "IdxIterator::inc(" << -1 << ") dim out of bounds!\n";
throw 1;
}
ie move the definition to namespace-scope.
Create a helper struct outside of class
template<dim>
struct inc {
template<class cur, end>
inline static void foo(cur curIdx, end endIdx) {
curIdx[dim]++;
if (curIdx[dim] > endIdx[dim]) {
inc<dim-1>::foo(curIdx, endIdx);
}
}
};
template<>
struct inc<0> {
template<class cur, end>
inline static void foo(cur, end) {
//terminate
}
};
class IdxIterator {
template<int i>
void inc() {
static_assert(i > 0, "error out of bounds");
int<i>::foo(/*params*/);
}
};
Note if you are using the GCC you may __attribute__((always_inline))to force inlining.
Related
This question already has answers here:
Why function template cannot be partially specialized?
(4 answers)
C++ function template partial specialization?
(7 answers)
Closed 25 days ago.
I have a template function and a partial specialized version of it like this:
template<typename _T>
int values_in_range(const _T &beg, const _T &end) {
throw std::runtime_error("unsupoorted data type");
}
template<>
int values_in_range<int>(const int &beg, const int &end) {
return end - beg;
}
These code works good and then I want to extend the capabilities of the function to a template class that have two template parameters like this:
template<typename _Ty, size_t _Dims>
class vec {
// ...
};
How to partial specialize the function values_in_range for class vec with keeping the parameter _Dims unspecialized?
I try following code but encount error:
template<size_t _Dims>
int values_in_range<vec<int, _Dims>>(const vec<int, _Dims> &beg,
const vec<int, _Dims> &end) { // <-- compiling error
int ret = 0;
for (int i = 0; i < _Dims; ++i) {
//some computing here
}
return ret;
}
The error message:
error: non-class, non-variable partial specialization ‘values_in_range<vec<int, _Dims> >’ is not allowed
[build] 349 | int values_in_range<vec<int, _Dims>>(const vec<int, _Dims> &beg,
[build] | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
There is no partial specialization of function templates. Your first snippet of code is no partial specialization. You can partially specialize class templates. A function template can usually be refactored to a class template with operator().
template<typename _T>
struct values_in_range {
int operator()(const _T &beg, const _T &end) const {
throw 42;
}
};
template<>
struct values_in_range<int> {
int operator()(const int &beg, const int &end) const {
return end - beg;
}
};
template<typename _Ty, unsigned _Dims>
class vec {};
template<unsigned _Dims>
struct values_in_range<vec<int,_Dims>> {
int operator()(const vec<int, _Dims> &beg, const vec<int, _Dims> &end) const {
int ret = 0;
for (int i = 0; i < _Dims; ++i) {
//some computing here
}
return ret;
}
};
I am trying to write a variadic function template to calculate the byte size of a struct. This is going to be used in a network programming project I am working on. The first step I came up with this without the variadic template that works:
#include <cstdint>
#include <iostream>
struct Position
{
int32_t x;
int32_t y;
};
template<typename T>
inline std::size_t sizeT() { return sizeof(T); }
template<>
inline std::size_t sizeT<Position>() { return sizeT<int32_t>() + sizeT<int32_t>(); }
// if I change the definition of Position, I need to remember to change this function
int main(int argc, char* argv[])
{
std::cout << "int8_t:" << sizeT<int8_t>() << std::endl;
std::cout << "Position:" << sizeT<Position>() << std::endl;
return 0;
}
I compiled with g++ 9.3.0 on my Ubuntu 20.04 laptop and it worked fine. However if I try to make it a variadic template then I ran into different compilation errors. The point of this variadic template is to be able to write like the following, so that the implementation doesn't depend on the explicit knowledge of x and y's types.
Here is bad code #1
#include <cstdint>
#include <iostream>
struct Position
{
int32_t x;
int32_t y;
};
template<typename T>
inline std::size_t sizeT() { return sizeof(T); }
template<>
inline std::size_t sizeT<Position>()
{
return sizeT<decltype(Position::x), decltype(Position::y)>();
}
template<>
inline std::size_t sizeT<Position>() { return sizeT<int32_t>() + sizeT<int32_t>(); }
int main(int argc, char* argv[])
{
std::cout << "int8_t:" << sizeT<int8_t>() << std::endl;
std::cout << "Position:" << sizeT<Position>() << std::endl;
return 0;
}
I got
error: ambiguous template specialization 'sizeT' for 'std::size_t sizeT()
note: candidate are: 'template std::size_t sizeT()'
note: template<class T, class... Ts> std::size_t sizeT()'
Bad code #2. If I put the variadic template at a different location:
#include <cstdint>
#include <iostream>
struct Position
{
int32_t x;
int32_t y;
};
template<typename T>
inline std::size_t sizeT() { return sizeof(T); }
template<>
inline std::size_t sizeT<Position>() { return sizeT<int32_t>() + sizeT<int32_t>(); }
template<>
inline std::size_t sizeT<Position>()
{
return sizeT<decltype(Position::x), decltype(Position::y)>();
}
int main(int argc, char* argv[])
{
std::cout << "int8_t:" << sizeT<int8_t>() << std::endl;
std::cout << "Position:" << sizeT<Position>() << std::endl;
return 0;
}
I got
error: call of overloaded 'sizeT<int8_t>()' is ambiguous
note candidate: 'std::size_t sizeT() [with T=signed char; std::size_t = long unsigned int]'
note candidate: 'std::size_t sizeT() [with T=signed char; Ts={}; std::size_t = long unsigned int]'
error: call of overloaded 'sizeT()' is ambiguous
note candidate: 'std::size_t sizeT() [with T=Position; std::size_t = long unsigned int]'
note candidate: 'std::size_t sizeT() [with T=Position; Ts={}; std::size_t = long unsigned int]'
I kinda understand case #2 but I don't understand #1. How can I achieve this? Thank you so much in advance.
You don't use variadic...
You might do (C++17)
template <typename ... Ts>
constexpr std::size_t sizeT() { return (0 + ... + sizeof(Ts)); }
template <>
constexpr std::size_t sizeT<Position>()
{
return sizeT<decltype(Position::x), decltype(Position::y)>();
}
Demo
But sizeT<Position, Position> would return 2 * sizeof(Position) instead of 2 * sizeT<Position> (which are identical here).
You might do instead:
template <typename... Ts>
struct tag{};
template <typename T>
constexpr std::size_t sizeT(tag<T>) { return sizeof(T); }
template <typename ... Ts>
constexpr std::size_t sizeT(tag<Ts...>) { return (0 + ... + sizeT(tag<Ts>())); }
template <typename ... Ts>
constexpr std::size_t sizeT(tag<Ts>...) { return (0 + ... + sizeT(tag<Ts>())); }
constexpr std::size_t sizeT(tag<Position>)
{
return sizeT(tag<decltype(Position::x), decltype(Position::y)>());
}
Demo
I have the following types:
struct A { };
struct B { };
struct C { };
template <typename Class, uint16_t i>
struct def {
using message_type = Class;
static constexpr uint16_t tag = i;
};
and this tuple:
constexpr auto types = std::make_tuple(def<A, 1>(), def<B, 2>(), def<C, 3>());
Types A, B and C should be mapped to corresponding values (A -> 1 etc.). I want to create something (function, struct) that given object of one of these types will return proper value. I tried doing the following:
template <typename T>
struct gettag {
static decltype(T::tag) value(typename T::message_type const&) { return T::tag; }
};
template <typename... Args>
struct tagdb : public gettag<Args>... {
tagdb(std::tuple<Args...> const& t) { }
};
int main() {
tagdb t(types);
A a;
std::cout << t.value(a) << '\n';
}
This does not work, g++ claims that request for member value is ambiguous:
x.cc: In function ‘int main()’:
x.cc:29:17: error: request for member ‘value’ is ambiguous
29 | std::cout << t.value(a) << '\n';
| ^~~~~
x.cc:16:26: note: candidates are: ‘static decltype (T::tag) gettag<T>::value(const typename T::message_type&) [with T = def<C, 3>; decltype (T::tag) = const short unsigned int; typename T::message_type = C]’
16 | static decltype(T::tag) value(typename T::message_type const&) { return T::tag; }
| ^~~~~
x.cc:16:26: note: ‘static decltype (T::tag) gettag<T>::value(const typename T::message_type&) [with T = def<B, 2>; decltype (T::tag) = const short unsigned int; typename T::message_type = B]’
x.cc:16:26: note: ‘static decltype (T::tag) gettag<T>::value(const typename T::message_type&) [with T = def<A, 1>; decltype (T::tag) = const short unsigned int; typename T::message_type = A]’
I am a little surprised, especially since it clearly shows that each method is parameterized using different types.
Is there a way to make this solution work or should I completely change my approach? Note that what I want to avoid most is writing overloads for each type.
I suggest a solution without a std::tuple and gettag:
struct A { };
struct B { };
struct C { };
template <typename Class, std::uint16_t i>
struct def {
static constexpr std::uint16_t value(Class) {
return i;
}
};
template <typename... Tags>
struct tagdb : public Tags... {
using Tags::value...;
};
template<class... Tags>
constexpr auto make_tagdb(Tags...) {
return tagdb<Tags...>{};
}
// template<class... Tags>
// constexpr auto make_tagdb(std::tuple<Tags...>) {
// return tagdb<Tags...>{};
// }
constexpr auto tags = make_tagdb(def<A, 1>(), def<B, 2>(), def<C, 3>());
int main() {
A a;
std::cout << tags.value(a) << '\n'; // Output: 1
}
The problem is that you have template base classes, all of which declare a member with the same name. The easiest fix is just to pull all the base class value functions into the derived class:
using gettag<Args>::value...;
See https://godbolt.org/z/F_Prhg
I want to define a function template of a class template. The code looks like this.
template<int M>
struct test{
private:
int value;
template<int N = 2 * M>
friend auto foo(test const t){
test<N> r;
r.value = t.value;
return r;
}
};
int main(){
test<1> t;
foo(t);// expected to return test<2>
foo<1>(t);// expected to return test<1>
foo<3>(t);// expected to return test<3>
}
But it won't compile. Compared with previous problems, the following lists the differences.
The result of the function template involves another instatiation of the class template. It seems that the function template has to be defined outside. But I am not sure.
The function template uses default template arguments. So, if the function template is defined outside the class, a helper function template is required.
Compiling Errors with g++ -std=c++1z:
a.cpp: In instantiation of 'auto foo(test<M>) [with int N = 2; int M = 1]':
a.cpp:16:10: required from here
a.cpp:4:9: error: 'int test<2>::value' is private
int value;
^
a.cpp:9:17: error: within this context
r.value = t.value;
^
a.cpp: In instantiation of 'auto foo(test<M>) [with int N = 3; int M = 1]':
a.cpp:18:13: required from here
a.cpp:4:9: error: 'int test<3>::value' is private
int value;
^
a.cpp:9:17: error: within this context
r.value = t.value;
^
A possible workaround, yet not correct either.
template<int M>
struct test{
private:
int value;
template<int NA, int NR>
friend test<NR> foo_impl(test<NA> const&);
};
template<int NA, int NR>
test<NR> foo_impl(test<NA> const& t){
test<NR> r;
r.value = t.value;
return r;
}
template<int NR, int NA>
auto foo(test<NA> const& t){
return foo_impl<NA, NR>;
}
template<int NA>
auto foo(test<NA> const& t){
return foo_impl<NA, NA * 2>(t);
}
int main(){
test<1> t;
foo(t);
foo<3>(t);
foo<1>(t);
}
Error:
t.cpp: In function 'int main()':
t.cpp:31:13: error: call of overloaded 'foo(test<1>&)' is ambiguous
foo<1>(t);
^
t.cpp:18:6: note: candidate: auto foo(const test<NR>&) [with int NR = 1; int NA = 1]
auto foo(test<NA> const& t){
^
t.cpp:23:6: note: candidate: auto foo(const test<NA>&) [with int NA = 1]
auto foo(test<NA> const& t){
^
After your edit, one possible way (which may not be better than your workaround - but work for all call) would be to use a default value for the parameter N:
template<int M>
struct test{
private:
int value;
template<int K, int U, int P>
friend test<P> foo(test<U> const t);
};
template <int N = 0, int M, int P = ((N == 0) ? M * 2 : N)>
test<P> foo (test<M> const t) {
test<P> r;
r.value = t.value;
return r;
}
int main(){
test<1> t;
test<2> p1 = foo(t);
test<3> p2 = foo<3>(t);
test<1> p3 = foo<1>(t);
}
This may not be prettier than your version...
The problem here is that you are declaring foo<N> a friend of test<M> while you want it to be a friend of any test<...>. You should do the following:
template<int M>
struct test{
private:
int value;
template<int U, int K>
friend test<K> foo(test<U> const t);
};
template <int M, int N = 2 * M>
test<N> foo (test<M> const t) {
test<N> r;
r.value = t.value;
return r;
}
int main(){
test<1> t;
foo(t);
}
Here you are saying:
Any function test<K> foo<U, K> (test<U> const) is a friend of any test<...>.
How can I solve this problem?
#include <iostream>
using namespace std;
template<size_t I, size_t... T>
void fun()
{
cout<<I<<endl;
fun<T...>();
}
template<size_t I>
void fun()
{
cout<<I<<endl;
}
int main()
{
fun<1, 2>();
fun<3>();
return 0;
}
errors:
|21|error: call of overloaded 'fun()' is ambiguous|
|21|note: candidates are:|
|5|note: void fun() [with unsigned int I = 3u;
unsigned int ...T = {}]| |12|note: void fun() [with unsigned int I =
3u]| In instantiation of 'void fun() [with unsigned int I = 2u;
unsigned int ...T = {}]':| |8|required from 'void fun() [with
unsigned int I = 1u; unsigned int ...T = {2u}]'| |20|required from
here| |8|error: no matching function for call to 'fun()'| |8|note:
candidate is:| |5|note: template<unsigned int I, unsigned int ...T>
void fun()| |5|note: template argument deduction/substitution
failed:| |8|note: couldn't deduce template parameter 'I'|
Make the variadic version only accept two or more template arguments.
template<size_t I>
void fun()
{
cout<<I<<endl;
}
template<size_t I1, size_t I2, size_t... T>
void fun()
{
cout<<I1<<endl;
fun<I2, T...>();
}