C++ check if statement can be evaluated constexpr - c++

Is there a method to decide whether something can be constexpr evaluated, and use the result as a constexpr boolean? My simplified use case is as follows:
template <typename base>
class derived
{
template<size_t size>
void do_stuff() { (...) }
void do_stuff(size_t size) { (...) }
public:
void execute()
{
if constexpr(is_constexpr(base::get_data())
{
do_stuff<base::get_data()>();
}
else
{
do_stuff(base::get_data());
}
}
}
My target is C++2a.
I found the following reddit thread, but I'm not a big fan of the macros. https://www.reddit.com/r/cpp/comments/7c208c/is_constexpr_a_macro_that_check_if_an_expression/

Here's another solution, which is more generic (applicable to any expression, without defining a separate template each time).
This solution leverages that (1) lambda expressions can be constexpr as of C++17 (2) the type of a captureless lambda is default constructible as of C++20.
The idea is, the overload that returns true is selected when and only when Lambda{}() can appear within a template argument, which effectively requires the lambda invocation to be a constant expression.
template<class Lambda, int=(Lambda{}(), 0)>
constexpr bool is_constexpr(Lambda) { return true; }
constexpr bool is_constexpr(...) { return false; }
template <typename base>
class derived
{
// ...
void execute()
{
if constexpr(is_constexpr([]{ base::get_data(); }))
do_stuff<base::get_data()>();
else
do_stuff(base::get_data());
}
}

Not exactly what you asked (I've developer a custom type trait specific for a get_value() static method... maybe it's possible to generalize it but, at the moment, I don't know how) but I suppose you can use SFINAE and make something as follows
#include <iostream>
#include <type_traits>
template <typename T>
constexpr auto icee_helper (int)
-> decltype( std::integral_constant<decltype(T::get_data()), T::get_data()>{},
std::true_type{} );
template <typename>
constexpr auto icee_helper (long)
-> std::false_type;
template <typename T>
using isConstExprEval = decltype(icee_helper<T>(0));
template <typename base>
struct derived
{
template <std::size_t I>
void do_stuff()
{ std::cout << "constexpr case (" << I << ')' << std::endl; }
void do_stuff (std::size_t i)
{ std::cout << "not constexpr case (" << i << ')' << std::endl; }
void execute ()
{
if constexpr ( isConstExprEval<base>::value )
do_stuff<base::get_data()>();
else
do_stuff(base::get_data());
}
};
struct foo
{ static constexpr std::size_t get_data () { return 1u; } };
struct bar
{ static std::size_t get_data () { return 2u; } };
int main ()
{
derived<foo>{}.execute(); // print "constexpr case (1)"
derived<bar>{}.execute(); // print "not constexpr case (2)"
}

template<auto> struct require_constant;
template<class T>
concept has_constexpr_data = requires { typename require_constant<T::get_data()>; };
This is basically what's used by std::ranges::split_view.

Related

Checking if template specialization of member function exists [duplicate]

Is there a way to establish at compile time if a certain function template was specialized?
For example, assume the following function template:
template<size_t N>
void foo();
I want to test if foo<42> was specialized. Note that the declaration above doesn't contain any default implementation.
I tried SFINAE but couldn't find a condition on the function that the compiler cannot deduce from its declaration.
Is there a way to establish in compile time if a certain template function was specialized?
With a function... I don't think so.
But if you create a functor, you can add a static const member (is_specialized, in the following example) that can give you this information
#include <iostream>
template <std::size_t N>
struct foo
{
static constexpr bool is_specialized { false };
void operator() () const
{ std::cout << "- generic (" << N << ") foo struct" << std::endl; }
};
template <>
struct foo<42U>
{
static constexpr bool is_specialized { true };
void operator() () const
{ std::cout << "- specialized (42) foo struct" << std::endl; }
};
int main()
{
foo<17U>()(); // print - generic (17) foo struct
foo<42U>()(); // print - specialized (42) foo struct
std::cout << foo<17U>::is_specialized << std::endl; // print 0
std::cout << foo<42U>::is_specialized << std::endl; // print 1
}
--- EDIT ---
Following the suggestion from Quentin (thanks again!) I've developed another functor-based solution that use something, to detect if the functor is generic or specialize, that is added only in the generic functor. In this case, a type instead a bool constant.
template <std::size_t N>
struct foo
{
// im_not_specialized is added only in the generic version!
using im_not_specialized = void;
void operator () () const
{ std::cout << "- generic (" << N << ") foo struct" << std::endl; }
};
template <>
struct foo<42U>
{
void operator () () const
{ std::cout << "- specialized (42) foo struct" << std::endl; }
};
This type can be used via SFINAE and I propose an example based on a constexpr isSpecialized() template function (with an helper function)
template <typename F>
constexpr bool isSpecializedHelper
(int, typename F::im_not_specialized const * = nullptr)
{ return false; }
template <typename F>
constexpr bool isSpecializedHelper (long)
{ return true; }
template <typename F>
constexpr bool isSpecialized ()
{ return isSpecializedHelper<F>(0); }
This require a little more work but isSpecialized() can be reused with different functors (im_not_specialized type based)
The following is a full working example
#include <iostream>
template <std::size_t N>
struct foo
{
// im_not_specialized is added only in the generic version!
using im_not_specialized = void;
void operator () () const
{ std::cout << "- generic (" << N << ") foo struct" << std::endl; }
};
template <>
struct foo<42U>
{
void operator () () const
{ std::cout << "- specialized (42) foo struct" << std::endl; }
};
template <typename F>
constexpr bool isSpecializedHelper
(int, typename F::im_not_specialized const * = nullptr)
{ return false; }
template <typename F>
constexpr bool isSpecializedHelper (long)
{ return true; }
template <typename F>
constexpr bool isSpecialized ()
{ return isSpecializedHelper<F>(0); }
int main()
{
foo<17U>()(); // print - generic (17) foo struct
foo<42U>()(); // print - specialized (42) foo struct
constexpr auto isSp17 = isSpecialized<foo<17U>>();
constexpr auto isSp42 = isSpecialized<foo<42U>>();
std::cout << isSp17 << std::endl; // print 0
std::cout << isSp42 << std::endl; // print 1
}
If you mark the base function as deleted (= delete), you can detect if it has been specialized using SFINAE (assuming the specialization itself is not deleted)
An expression like decltype(foo<N>()) will result in a substitution failure if foo<N> is marked as deleted. If you provide a specialization that is not deleted on the other hand the expression will not result in an error.
Using this you can create a simple trait class to check if foo has been specialized for a specific set of template parameters:
template<std::size_t N, class = void>
struct is_foo_specialized : std::false_type {};
template<std::size_t N>
struct is_foo_specialized<N, decltype(foo<N>(), void())> : std::true_type {};
1. Basic examples
C++11: godbolt
#include <type_traits>
template<std::size_t N>
void foo() = delete;
template<>
void foo<1>() { }
template<std::size_t N, class = void>
struct is_foo_specialized : std::false_type {};
template<std::size_t N>
struct is_foo_specialized<N, decltype(foo<N>(), void())> : std::true_type {};
int main()
{
static_assert(!is_foo_specialized<0>::value, ""); // foo<0> is not specialized
static_assert(is_foo_specialized<1>::value, ""); // foo<1> IS specialized
}
With C++20 you could also use a concept for this, e.g.: godbolt
#include <type_traits>
template<std::size_t N>
void foo() = delete;
template<>
void foo<1>() { }
template<std::size_t N>
concept is_foo_specialized = requires { foo<N>(); };
int main()
{
static_assert(!is_foo_specialized<0>); // foo<0> is not specialized
static_assert(is_foo_specialized<1>); // foo<1> IS specialized
}
2. Providing a default implementation
Due to the function being = delete'd it can't have a default implementation.
If you do require a default implementation for the function, you could use 2 functions instead:
one that is = delete'd (so SFINAE can detect it)
and another one that implements the default behaviour and forwards to the other if a specialization exists
C++11: godbolt
#include <type_traits>
#include <iostream>
template<std::size_t N>
void foo_specialized() = delete;
template<>
void foo_specialized<1>() { std::cout << "CUSTOMIZED!" << std::endl; }
template<std::size_t N, class = void>
struct is_foo_specialized : std::false_type {};
template<std::size_t N>
struct is_foo_specialized<N, decltype(foo_specialized<N>(), void())> : std::true_type {};
template<std::size_t N>
typename std::enable_if<!is_foo_specialized<N>::value>::type foo() {
std::cout << "DEFAULT!" << std::endl;
}
template<std::size_t N>
typename std::enable_if<is_foo_specialized<N>::value>::type foo() {
foo_specialized<N>();
}
int main()
{
foo<0>(); // -> DEFAULT!
foo<1>(); // -> CUSTOMIZED!
}
Or with C++20: godbolt
#include <type_traits>
#include <iostream>
template<std::size_t N>
void foo_specialize() = delete;
template<>
void foo_specialize<1>() { std::cout << "CUSTOMIZED!" << std::endl; }
template<std::size_t N>
concept is_foo_specialized = requires { foo_specialize<N>(); };
template<std::size_t N> requires (!is_foo_specialized<N>)
void foo() {
std::cout << "DEFAULT!" << std::endl;
}
template<std::size_t N> requires (is_foo_specialized<N>)
void foo() {
foo_specialize<N>();
}
int main()
{
foo<0>(); // -> DEFAULT!
foo<1>(); // -> CUSTOMIZED!
}
3. Compile-time shenanigans
This can of course also be used to iterate the specializations (within a certain limit) - or like you asked in the comments to find the nearest specialization of the function.
nearest_foo_specialized in this example will iterate over a range of values for N and check if a specialization of foo exists for this value.
N is the value where we want to start the search
SearchRange determines how many specializations will be checked (both up and down) from the provided N value (in this example we check for N's +/- 10)
CurrentDistance keeps track how far we've already searched from our starting N value, so we don't exceed the specified SearchRange
The last template parameter is used for SFINAE
e.g.:
nearest_foo_specialized<100, 10> would check for specializations of foo between N = 90 and N = 110, returning the one that is closer to 100 (prefering lower N values in case of a draw)
Example C++11: godbolt
#include <type_traits>
#include <iostream>
#include <utility>
template<std::size_t N>
void foo() = delete;
template<>
void foo<5>() { std::cout << 5 << std::endl; }
template<>
void foo<10>() { std::cout << 10 << std::endl; }
template<>
void foo<15>() { std::cout << 15 << std::endl; }
template<std::size_t N, class = void>
struct is_foo_specialized : std::false_type {};
template<std::size_t N>
struct is_foo_specialized<N, decltype(foo<N>(), void())> : std::true_type {};
template<std::size_t N, std::size_t SearchRange = 10, std::size_t CurrentDistance = 0, class = void>
struct nearest_foo_specialized {
static const std::size_t index = 0; // an index for which foo<> is specialized, if value is true.
static const std::size_t distance = CurrentDistance; // distance from original N
static const bool value = false; // have we found a specialization yet?
};
// Found a match -> Report Success
template<std::size_t N, std::size_t SearchRange, std::size_t CurrentDistance>
struct nearest_foo_specialized<N, SearchRange, CurrentDistance, typename std::enable_if< CurrentDistance <= SearchRange && is_foo_specialized<N>::value >::type> {
static const std::size_t index = N;
static const std::size_t distance = CurrentDistance;
static const bool value = true;
};
// No match found -> recurse until SearchRange limit
template<std::size_t N, std::size_t SearchRange, std::size_t CurrentDistance>
struct nearest_foo_specialized<N, SearchRange, CurrentDistance, typename std::enable_if< CurrentDistance < SearchRange && !is_foo_specialized<N>::value >::type> {
typedef nearest_foo_specialized<N - 1, SearchRange, CurrentDistance + 1> down;
typedef nearest_foo_specialized<N + 1, SearchRange, CurrentDistance + 1> up;
static const std::size_t distance = down::distance < up::distance ? down::distance : up::distance;
static const std::size_t index = down::distance == distance && down::value ? down::index : up::index;
static const std::size_t value = down::distance == distance && down::value ? down::value : up::value;
};
// calls the nearest foo() specialization (up to 10 away from the specified N)
template<std::size_t N>
typename std::enable_if<nearest_foo_specialized<N>::value>::type call_nearest_foo() {
foo<nearest_foo_specialized<N>::index>();
}
template<std::size_t N>
typename std::enable_if<!nearest_foo_specialized<N>::value>::type call_nearest_foo() {
static_assert(N!=N, "No nearest foo() specialization found!");
}
int main() {
call_nearest_foo<7>(); // calls foo<5>()
call_nearest_foo<8>(); // calls foo<10>()
call_nearest_foo<11>(); // calls foo<10>()
call_nearest_foo<15>(); // calls foo<15>()
call_nearest_foo<25>(); // calls foo<15>()
// call_nearest_foo<26>(); // error: No nearest foo() (only searching up to 10 up / down)
}

How to check a type has constexpr constructor

I want my class use another implementation for types don't have constexpr constructor.
like this:
template <typename A>
class foo
{
public:
// if A has constexpr constructor
constexpr foo() :_flag(true) { _data._a = A(); }
// else
constexpr foo() : _flag(false) { _data.x = 0; }
~foo(){}
bool _flag;
union _data_t
{
_data_t() {} // nothing, because it's just an example
~_data_t() {}
A _a;
int x;
}_data;
};
To achieve what the title says, I try this:
template<typename _t, _t = _t()>
constexpr bool f()
{
return true;
}
template<typename _t>
constexpr bool f()
{
return false;
}
It works well for types haven't constexpr constructor.
But for other types it causes a compile error with ambiguous overloads.
so how can I check?
I suppose you can use SFINAE together with the power of the comma operator
Following your idea, you can rewrite your f() functions as follows
template <typename T, int = (T{}, 0)>
constexpr bool f (int)
{ return true; }
template <typename>
constexpr bool f (long)
{ return false; }
Observe the trick: int = (T{}, 0) for the second template argument
This way f() is enabled (power of the comma operator) only if T{} can be constexpr constructed (because (T{}, 0) is the argument for a template parameter), otherwise SFINAE wipe away the first version of f().
And observe that the fist version of f() receive an unused int where the second one receive a long. This way the first version is preferred, when available, calling f() with an int; the second one is selected, as better than nothing solution, when the first one is unavailable (when the first template argument isn't constexpr default constructible).
Now you can construct two template constructors for foo that you can alternatively enable/disable according the fact the template parameter T (defaulted to A) is or isn't constexpr constructible
template <typename T = A,
std::enable_if_t<f<T>(0), std::nullptr_t> = nullptr>
constexpr foo() { std::cout << "constexpr" << std::endl; }
template <typename T = A,
std::enable_if_t<not f<T>(0), std::nullptr_t> = nullptr>
constexpr foo() { std::cout << "not constexpr" << std::endl; }
The following is a full compiling example (C++14 or newer, but you can modify it for C++11):
#include <iostream>
#include <type_traits>
template <typename T, int = (T{}, 0)>
constexpr bool f (int)
{ return true; }
template <typename>
constexpr bool f (long)
{ return false; }
template <typename A>
struct foo
{
template <typename T = A,
std::enable_if_t<f<T>(0), std::nullptr_t> = nullptr>
constexpr foo() { std::cout << "constexpr" << std::endl; }
template <typename T = A,
std::enable_if_t<not f<T>(0), std::nullptr_t> = nullptr>
constexpr foo() { std::cout << "not constexpr" << std::endl; }
};
struct X1 { constexpr X1 () {} };
struct X2 { X2 () {} };
int main()
{
foo<X1> f1; // print "constexpr"
foo<X2> f2; // print "not constexpr"
}

How to check for static member variable template?

I need to define a class, foo, with a static member variable template, foo::static_variable_template<T>. This member should only exist when T fulfills certain requirements. For example, when the constexpr static function T::constexpr_static_function() exists. Otherwise, foo::static_variable_template<T> should not exist. Moreover, I would like to be able to test for the existence of foo::static_variable_template<T> at compile-time via SFINAE.
Here is an approximation of what I would like to do:
#include <iostream>
struct foo
{
template<class T>
static constexpr int static_variable_template =
T::constexpr_static_function();
// XXX this works but requires a second defaulted template parameter
// template<class T, int = T::constexpr_static_function()>
// static constexpr int static_variable_template =
// T::constexpr_static_function();
};
struct has_constexpr_static_function
{
static constexpr int constexpr_static_function() { return 42; }
};
struct hasnt_constexpr_static_function
{
};
template<class T, class U,
int = T::template static_variable_template<U>>
void test_for_static_variable_template(int)
{
std::cout << "yes it has\n";
}
template<class T, class U>
void test_for_static_variable_template(...)
{
std::cout << "no it hasn't\n";
}
int main()
{
test_for_static_variable_template<foo, has_constexpr_static_function>(0);
test_for_static_variable_template<foo, hasnt_constexpr_static_function>(0);
}
This approximation nearly works, but only if foo::static_variable_template has a second, defaulted template parameter. Because this second parameter is an implementation detail, I'd like to hide it from the public interface of foo::static_variable_template.
Is this possible in C++17?
I am not sure if your intent is to initialise foo::static_variable_template with 0 if T::constexpr_static_function() is missing or you want to disable it entirely. In case of the former, this might be useful. For example, this (clunky) solution works (requires C++17 for if constexpr; note that your variable is now a function):
#include <iostream>
template <typename T>
class has_func
{
typedef char does;
typedef long doesnt;
template <typename C> static does test( decltype(&C::constexpr_static_function) );
template <typename C> static doesnt test(...);
public:
static constexpr bool value()
{
return sizeof(test<T>(0)) == sizeof(char);
}
};
struct foo
{
template<class T>
static constexpr int static_variable_template()
{
if constexpr (has_func<T>::value())
{
return T::constexpr_static_function();
}
return 0;
}
// XXX this works but requires a second defaulted template parameter
// template<class T, int = T::constexpr_static_function()>
// static constexpr int static_variable_template =
// T::constexpr_static_function();
};
struct has_constexpr_static_function
{
static constexpr int constexpr_static_function() { return 42; }
};
struct hasnt_constexpr_static_function
{
};
template<class T, class U>
void test_for_static_variable_template(...)
{
if constexpr (has_func<U>::value())
{
std::cout << "yes it has\n";
}
else
{
std::cout << "no it hasn't\n";
}
}
int main()
{
std::cout << foo::static_variable_template<has_constexpr_static_function>() << "\n";
std::cout << foo::static_variable_template<hasnt_constexpr_static_function>() << "\n";
/// Original test
test_for_static_variable_template<foo, has_constexpr_static_function>(0);
test_for_static_variable_template<foo, hasnt_constexpr_static_function>(0);
}
Prints
42
0
yes it has
no it hasn't
Tested with clang 5.0.1.
In case you want to disable foo::static_variable_template entirely, you might need to use std::enable_if:
#include <iostream>
template <typename T>
class has_func
{
typedef char does;
typedef long doesnt;
template <typename C> static does test( decltype(&C::constexpr_static_function) );
template <typename C> static doesnt test(...);
public:
static constexpr bool value()
{
return sizeof(test<T>(0)) == sizeof(char);
}
};
struct foo
{
template<class T, typename std::enable_if<has_func<T>::value()>::type ...>
static constexpr int static_variable_template()
{
if constexpr (has_func<T>::value())
{
return T::constexpr_static_function();
}
return 0;
}
// XXX this works but requires a second defaulted template parameter
// template<class T, int = T::constexpr_static_function()>
// static constexpr int static_variable_template =
// T::constexpr_static_function();
};
struct has_constexpr_static_function
{
static constexpr int constexpr_static_function() { return 42; }
};
struct hasnt_constexpr_static_function
{
};
template<class T, class U>
void test_for_static_variable_template(...)
{
if constexpr (has_func<U>::value())
{
std::cout << "yes it has\n";
}
else
{
std::cout << "no it hasn't\n";
}
}
int main()
{
std::cout << foo::static_variable_template<has_constexpr_static_function>() << "\n";
// We can't print this because it doesn't exist.
// std::cout << foo::static_variable_template<hasnt_constexpr_static_function>() << "\n";
/// Original test
test_for_static_variable_template<foo, has_constexpr_static_function>(0);
test_for_static_variable_template<foo, hasnt_constexpr_static_function>(0);
}
In this line of thought, I am not sure if you can disable a static template variable with std::enable_if. To quote the great Riemann, "I have for the time being, after some fleeting vain attempts, provisionally put aside the search for this..."

How to check if a function template has been specialized?

Is there a way to establish at compile time if a certain function template was specialized?
For example, assume the following function template:
template<size_t N>
void foo();
I want to test if foo<42> was specialized. Note that the declaration above doesn't contain any default implementation.
I tried SFINAE but couldn't find a condition on the function that the compiler cannot deduce from its declaration.
Is there a way to establish in compile time if a certain template function was specialized?
With a function... I don't think so.
But if you create a functor, you can add a static const member (is_specialized, in the following example) that can give you this information
#include <iostream>
template <std::size_t N>
struct foo
{
static constexpr bool is_specialized { false };
void operator() () const
{ std::cout << "- generic (" << N << ") foo struct" << std::endl; }
};
template <>
struct foo<42U>
{
static constexpr bool is_specialized { true };
void operator() () const
{ std::cout << "- specialized (42) foo struct" << std::endl; }
};
int main()
{
foo<17U>()(); // print - generic (17) foo struct
foo<42U>()(); // print - specialized (42) foo struct
std::cout << foo<17U>::is_specialized << std::endl; // print 0
std::cout << foo<42U>::is_specialized << std::endl; // print 1
}
--- EDIT ---
Following the suggestion from Quentin (thanks again!) I've developed another functor-based solution that use something, to detect if the functor is generic or specialize, that is added only in the generic functor. In this case, a type instead a bool constant.
template <std::size_t N>
struct foo
{
// im_not_specialized is added only in the generic version!
using im_not_specialized = void;
void operator () () const
{ std::cout << "- generic (" << N << ") foo struct" << std::endl; }
};
template <>
struct foo<42U>
{
void operator () () const
{ std::cout << "- specialized (42) foo struct" << std::endl; }
};
This type can be used via SFINAE and I propose an example based on a constexpr isSpecialized() template function (with an helper function)
template <typename F>
constexpr bool isSpecializedHelper
(int, typename F::im_not_specialized const * = nullptr)
{ return false; }
template <typename F>
constexpr bool isSpecializedHelper (long)
{ return true; }
template <typename F>
constexpr bool isSpecialized ()
{ return isSpecializedHelper<F>(0); }
This require a little more work but isSpecialized() can be reused with different functors (im_not_specialized type based)
The following is a full working example
#include <iostream>
template <std::size_t N>
struct foo
{
// im_not_specialized is added only in the generic version!
using im_not_specialized = void;
void operator () () const
{ std::cout << "- generic (" << N << ") foo struct" << std::endl; }
};
template <>
struct foo<42U>
{
void operator () () const
{ std::cout << "- specialized (42) foo struct" << std::endl; }
};
template <typename F>
constexpr bool isSpecializedHelper
(int, typename F::im_not_specialized const * = nullptr)
{ return false; }
template <typename F>
constexpr bool isSpecializedHelper (long)
{ return true; }
template <typename F>
constexpr bool isSpecialized ()
{ return isSpecializedHelper<F>(0); }
int main()
{
foo<17U>()(); // print - generic (17) foo struct
foo<42U>()(); // print - specialized (42) foo struct
constexpr auto isSp17 = isSpecialized<foo<17U>>();
constexpr auto isSp42 = isSpecialized<foo<42U>>();
std::cout << isSp17 << std::endl; // print 0
std::cout << isSp42 << std::endl; // print 1
}
If you mark the base function as deleted (= delete), you can detect if it has been specialized using SFINAE (assuming the specialization itself is not deleted)
An expression like decltype(foo<N>()) will result in a substitution failure if foo<N> is marked as deleted. If you provide a specialization that is not deleted on the other hand the expression will not result in an error.
Using this you can create a simple trait class to check if foo has been specialized for a specific set of template parameters:
template<std::size_t N, class = void>
struct is_foo_specialized : std::false_type {};
template<std::size_t N>
struct is_foo_specialized<N, decltype(foo<N>(), void())> : std::true_type {};
1. Basic examples
C++11: godbolt
#include <type_traits>
template<std::size_t N>
void foo() = delete;
template<>
void foo<1>() { }
template<std::size_t N, class = void>
struct is_foo_specialized : std::false_type {};
template<std::size_t N>
struct is_foo_specialized<N, decltype(foo<N>(), void())> : std::true_type {};
int main()
{
static_assert(!is_foo_specialized<0>::value, ""); // foo<0> is not specialized
static_assert(is_foo_specialized<1>::value, ""); // foo<1> IS specialized
}
With C++20 you could also use a concept for this, e.g.: godbolt
#include <type_traits>
template<std::size_t N>
void foo() = delete;
template<>
void foo<1>() { }
template<std::size_t N>
concept is_foo_specialized = requires { foo<N>(); };
int main()
{
static_assert(!is_foo_specialized<0>); // foo<0> is not specialized
static_assert(is_foo_specialized<1>); // foo<1> IS specialized
}
2. Providing a default implementation
Due to the function being = delete'd it can't have a default implementation.
If you do require a default implementation for the function, you could use 2 functions instead:
one that is = delete'd (so SFINAE can detect it)
and another one that implements the default behaviour and forwards to the other if a specialization exists
C++11: godbolt
#include <type_traits>
#include <iostream>
template<std::size_t N>
void foo_specialized() = delete;
template<>
void foo_specialized<1>() { std::cout << "CUSTOMIZED!" << std::endl; }
template<std::size_t N, class = void>
struct is_foo_specialized : std::false_type {};
template<std::size_t N>
struct is_foo_specialized<N, decltype(foo_specialized<N>(), void())> : std::true_type {};
template<std::size_t N>
typename std::enable_if<!is_foo_specialized<N>::value>::type foo() {
std::cout << "DEFAULT!" << std::endl;
}
template<std::size_t N>
typename std::enable_if<is_foo_specialized<N>::value>::type foo() {
foo_specialized<N>();
}
int main()
{
foo<0>(); // -> DEFAULT!
foo<1>(); // -> CUSTOMIZED!
}
Or with C++20: godbolt
#include <type_traits>
#include <iostream>
template<std::size_t N>
void foo_specialize() = delete;
template<>
void foo_specialize<1>() { std::cout << "CUSTOMIZED!" << std::endl; }
template<std::size_t N>
concept is_foo_specialized = requires { foo_specialize<N>(); };
template<std::size_t N> requires (!is_foo_specialized<N>)
void foo() {
std::cout << "DEFAULT!" << std::endl;
}
template<std::size_t N> requires (is_foo_specialized<N>)
void foo() {
foo_specialize<N>();
}
int main()
{
foo<0>(); // -> DEFAULT!
foo<1>(); // -> CUSTOMIZED!
}
3. Compile-time shenanigans
This can of course also be used to iterate the specializations (within a certain limit) - or like you asked in the comments to find the nearest specialization of the function.
nearest_foo_specialized in this example will iterate over a range of values for N and check if a specialization of foo exists for this value.
N is the value where we want to start the search
SearchRange determines how many specializations will be checked (both up and down) from the provided N value (in this example we check for N's +/- 10)
CurrentDistance keeps track how far we've already searched from our starting N value, so we don't exceed the specified SearchRange
The last template parameter is used for SFINAE
e.g.:
nearest_foo_specialized<100, 10> would check for specializations of foo between N = 90 and N = 110, returning the one that is closer to 100 (prefering lower N values in case of a draw)
Example C++11: godbolt
#include <type_traits>
#include <iostream>
#include <utility>
template<std::size_t N>
void foo() = delete;
template<>
void foo<5>() { std::cout << 5 << std::endl; }
template<>
void foo<10>() { std::cout << 10 << std::endl; }
template<>
void foo<15>() { std::cout << 15 << std::endl; }
template<std::size_t N, class = void>
struct is_foo_specialized : std::false_type {};
template<std::size_t N>
struct is_foo_specialized<N, decltype(foo<N>(), void())> : std::true_type {};
template<std::size_t N, std::size_t SearchRange = 10, std::size_t CurrentDistance = 0, class = void>
struct nearest_foo_specialized {
static const std::size_t index = 0; // an index for which foo<> is specialized, if value is true.
static const std::size_t distance = CurrentDistance; // distance from original N
static const bool value = false; // have we found a specialization yet?
};
// Found a match -> Report Success
template<std::size_t N, std::size_t SearchRange, std::size_t CurrentDistance>
struct nearest_foo_specialized<N, SearchRange, CurrentDistance, typename std::enable_if< CurrentDistance <= SearchRange && is_foo_specialized<N>::value >::type> {
static const std::size_t index = N;
static const std::size_t distance = CurrentDistance;
static const bool value = true;
};
// No match found -> recurse until SearchRange limit
template<std::size_t N, std::size_t SearchRange, std::size_t CurrentDistance>
struct nearest_foo_specialized<N, SearchRange, CurrentDistance, typename std::enable_if< CurrentDistance < SearchRange && !is_foo_specialized<N>::value >::type> {
typedef nearest_foo_specialized<N - 1, SearchRange, CurrentDistance + 1> down;
typedef nearest_foo_specialized<N + 1, SearchRange, CurrentDistance + 1> up;
static const std::size_t distance = down::distance < up::distance ? down::distance : up::distance;
static const std::size_t index = down::distance == distance && down::value ? down::index : up::index;
static const std::size_t value = down::distance == distance && down::value ? down::value : up::value;
};
// calls the nearest foo() specialization (up to 10 away from the specified N)
template<std::size_t N>
typename std::enable_if<nearest_foo_specialized<N>::value>::type call_nearest_foo() {
foo<nearest_foo_specialized<N>::index>();
}
template<std::size_t N>
typename std::enable_if<!nearest_foo_specialized<N>::value>::type call_nearest_foo() {
static_assert(N!=N, "No nearest foo() specialization found!");
}
int main() {
call_nearest_foo<7>(); // calls foo<5>()
call_nearest_foo<8>(); // calls foo<10>()
call_nearest_foo<11>(); // calls foo<10>()
call_nearest_foo<15>(); // calls foo<15>()
call_nearest_foo<25>(); // calls foo<15>()
// call_nearest_foo<26>(); // error: No nearest foo() (only searching up to 10 up / down)
}

templates, decltype and non-classtypes

I have a function definition like so
template <typename T>
auto print(T t) -> decltype(t.print()) {
return t.print();
}
The idea is that the argument must be of type T and must have the print function. This print function could return anything, explaining the need of decltype. So for example you can do:
struct Foo
{
int print()
{
return 42;
}
};
struct Bar
{
std::string print()
{
return "The answer...";
}
};
...
std::cout << print(Foo()) << std::endl;
std::cout << print(Bar()) << std::endl;
/* outputs:
42
The answer...
*/
I read that templates cannot do runtime instantiation and that you can have the classes derive from a base class, then determine their types to see what template argument to use. However, how would I do this for a non-class type? The idea is to be able to have:
template <typename T>
T print(T t) {
return t;
}
as well, but this gives me ambiguous overload errors. Qualifying doesn't work, ie print<Foo>. And the other got'cha is, what if I had a functor like:
struct Foo
{
virtual int print();
operator int() const
{
return 42;
}
};
How does it decide now?
So my question is, is it possible to resolve all these ambiguities with templates, or do I have to write a bunch of redundant code?
Tests
I incrementally added tests, copy/pasting each edit'ed solution from below. Here are the results:
With the following classes:
struct Foo
{
int print()
{
return 42;
}
operator int() const
{
return 32;
}
};
struct Bar
{
std::string print()
{
return "The answer...";
}
operator int() const
{
return (int)Foo();
}
};
struct Baz
{
operator std::string() const
{
return std::string("The answer...");
}
};
And the following test output:
std::cout << print(Foo()) << std::endl;
std::cout << print(Bar()) << std::endl;
std::cout << print(42) << std::endl;
std::cout << print((int)Foo()) << std::endl;
std::cout << print("The answer...") << std::endl;
std::cout << print(std::string("The answer...")) << std::endl;
std::cout << print((int)Bar()) << std::endl;
std::cout << print((std::string)Baz()) << std::endl;
Both correctly output:
42
The answer...
42
32
The answer...
The answer...
32
The answer...
You could adopt the following approach, which invokes a print() member function on the input if such a member function exists, otherwise it will return the input itself:
namespace detail
{
template<typename T, typename = void>
struct print_helper
{
static T print(T t) {
return t;
}
};
template<typename T>
struct print_helper<T, decltype(std::declval<T>().print(), (void)0)>
{
static auto print(T t) -> decltype(t.print()) {
return t.print();
}
};
}
template<typename T>
auto print(T t) -> decltype(detail::print_helper<T>::print(t))
{
return detail::print_helper<T>::print(t);
}
Here is a live example.
Simple solution using manual overloading for each type which you want to print directly:
Define your first implementation which calls T::print(). Use overloading to specify alternative implementations for all types which don't have this function. I don't recommend this solution, but it's very easy to understand.
template<typename T>
auto print(T t) -> decltype(t.print()) {
return t.print();
}
int print(int t) {
return t;
}
std::string print(std::string t) {
return t;
}
// ... and so on, for each type you want to support ...
More advanced solution using SFINAE which uses T::print() automatically if and only if it's there:
First, define a trait which can decide if your type has a function print(). Basically, this trait inherits from either std::true_type or std::false_type, depending on the decision being made in some helper class (_test_print). Then, use this type trait in an enable_if compile-time decision which defines only one of the two cases and hides the other one (so this is not overloading).
// Type trait "has_print" which checks if T::print() is available:
struct _test_print {
template<class T> static auto test(T* p) -> decltype(p->print(), std::true_type());
template<class> static auto test(...) -> std::false_type;
};
template<class T> struct has_print : public decltype(_test_print::test<T>(0)) {};
// Definition of print(T) if T has T::print():
template<typename T>
auto print(T t) -> typename std::enable_if<has_print<T>::value, decltype(t.print())>::type {
return t.print();
}
// Definition of print(T) if T doesn't have T::print():
template<typename T>
auto print(T t) -> typename std::enable_if<!has_print<T>::value, T>::type {
return t;
}
Have a look at the live demo.