Reputation: 60421
Considering the following example (I posted it for several different questions today) :
#include <iostream>
#include <vector>
#include <array>
#include <type_traits>
// Version A
template<typename T>
constexpr unsigned int f(const T&)
{
return 1;
}
// Version B
template<typename... T1, template<typename...> class T>
constexpr unsigned int f(const T<T1...>&)
{
return 2;
}
// Version C
template<typename T1, template<typename, unsigned int...> class T, unsigned int... N>
constexpr unsigned int f(const T<T1, N...>&)
{
return 3;
}
// Main
int main(int argc, char* argv[])
{
std::integral_constant<int, f(double())> a;
std::integral_constant<int, f(std::vector<double>())> b;
std::integral_constant<int, f(std::array<double, 3>())> c;
std::cout<<a<<b<<c<<std::endl; // The goal is to return 123
return 0;
}
This code does not compile and returns the following compilation error :
temporary of non-literal type 'std::vector<double>' in a constant expression
How to modify this code in order to compile it ?
Note : the goal is to convert a type that will be taken by the first version of the function to 1, a type that will be taken by the second version of the function to 2, etc...
Upvotes: 3
Views: 6852
Reputation: 13288
You need partial specialization wich is impossible on functions, wrapping them in a struct/class gets the job done :
#include <iostream>
#include <vector>
#include <array>
#include <type_traits>
// Version A
template<typename T>
struct f
{
constexpr static unsigned int execute()
{
return 1;
}
};
// Version B
template<template <typename ... > class Tpl, typename ... TplArgs>
struct f< Tpl<TplArgs...> >
{
constexpr static unsigned int execute()
{
return 2;
}
};
// Version C
template<template<typename, std::size_t...> class Tpl, typename FirstArg, std::size_t... N>
struct f< Tpl<FirstArg, N...> >
{
constexpr static unsigned int execute()
{
return 3;
}
};
// Main
int main(int argc, char* argv[])
{
std::integral_constant<int, f<double>::execute()> a;
std::integral_constant<int, f<std::vector<double>>::execute()> b;
std::integral_constant<int, f<std::array<double, 3>>::execute()> c;
std::cout << a << ' ' << b << ' ' << c << std::endl;
return 0;
}
Upvotes: 2
Reputation: 60421
I found the solution by using pointers instead of references :
// Version A
template<typename T>
constexpr unsigned int f(const T*)
{
return 1;
}
// Version B
template<typename... T1, template<typename...> class T>
constexpr unsigned int f(const T<T1...>*)
{
return 2;
}
// Version C
template<typename T1, template<typename, unsigned int...> class T, unsigned int... N>
constexpr unsigned int f(const T<T1, N...>*)
{
return 3;
}
// Main
int main(int argc, char* argv[])
{
std::vector<double> tmp;
std::integral_constant<int, f(static_cast<double*>(nullptr))> a;
std::integral_constant<int, f(static_cast<decltype(tmp)*>(nullptr))> b;
std::integral_constant<int, f(static_cast<std::array<double, 3>*>(nullptr)) > c;
std::cout<<a<<b<<c<<std::endl;
return 0;
}
Maybe it's not the most elegant way to do it but it works. If someone has an elegant equivalent of this, I am very interested in.
Upvotes: 0
Reputation: 12547
You cannot use temporary vector when defining constant:
int main(int argc, char* argv[])
{
std::integral_constant<int, f(double())> a;
std::vector<double> vec;
std::integral_constant<int, f(vec)> b;
std::integral_constant<int, f(std::array<double, 3>())> c;
std::cout << a << b << c;
return 0;
}
The thing is that compiler could possibly omit vector
creation, if vector's only purpose was passing to the constant expression function, but actually it cannot, because vector is not literal type.
std::array
is only a thing wrapper above c array, it has trivial constructor and destructor. As double is also literal type, array of doubles becomes literal.
Note, however, that if you define
struct A
{
A(){std::cout << "I'm so complicated A!\n"; }
}
you would not be able to use constructs:
int main(int argc, char* argv[])
{
std::integral_constant<int, f(A())> a;
std::integral_constant<int, f(std::array<A, 3>())> c;
std::cout << a << b << c;
return 0;
}
either, while
int main(int argc, char* argv[])
{
A a_v;
std::integral_constant<int, f(a_v)> a;
std::array<A, 3> c_v
std::integral_constant<int, f(c_v)> c;
std::cout << a << b << c;
return 0;
}
still would be possible.
Upvotes: 1
Reputation: 5127
The question to you is how much can we afford to modify this code? For instance, the following compiles, but is it what you were trying to do?
#include <iostream>
#include <vector>
#include <array>
#include <type_traits>
// Version A
template<typename T>
constexpr unsigned int f()
{
return 1;
}
// Version B
template<typename... T1, template<typename...> class T>
constexpr unsigned int f()
{
return 2;
}
// Version C
template<typename T1 = double, template<typename, unsigned int...> class T, unsigned int... N>
constexpr unsigned int f()
{
return 3;
}
// Main
int main(int argc, char* argv[])
{
std::integral_constant<int, f<double>()> a;
std::integral_constant<int, f<std::vector<double>>()> b;
std::integral_constant<int, f<std::array<double, 3>>()> c;
return 0;
}
Upvotes: 0