Specialize if value of a variable is known/unknown at compile time - c++

How to specialize a template function for the case that the value of one of its argument is known/unknown during compile time (before actually compile and run the program)?
I can't figure out how yet.
idea 1:
#include <type_traits>
#include <iostream>
int main(void){
int a; //value of a is not known at compile time
bool b = (a == a); //value of b is known at compile time.
std::is_assignable< constexpr bool, bool >::value
}
//g++ magic.cpp -std=c++14
//error: wrong number of template arguments (1, should be 2)
// std::is_assignable< constexpr bool, bool >::value
Idea 2:
#include <type_traits>
#include <iostream>
int main(void){
const int a=1;
int b = (a == a);
std::cout << __builtin_constant_p (a) << std::endl;
std::cout << __builtin_constant_p (b) << std::endl;
}
//prints 0 and 0.

Well, I guess you mean the type of the argument, right? Values don't matter for partial template specializations...
Then: This can't be done.
Parameter types for templates must be known at compile time. How else should the compiler produce the correct code?
Also for partial template specializations, the types must be known at compile time for the same reason.

I'm a bit late to the party, and my partial answer may not be very satisfying, but here goes:
Situations where we can't tell from inspection whether a value is known at compile time:
Non-template input values to constexpr functions
Members of types provided by template arguments
I don't know what to do about problem 1, but for problem 2 we can use SFINAE: looking for a specific member with known name (in the below example it's X), and trying to send it as a template argument, like so:
// like std::void_t, but for value inputs:
template <auto>
using v_to_void_t = void;
//////////////////////////////////////////////////////////////
template <class, class = void>
struct has_constexpr_X
: std::false_type {};
template <class T>
struct has_constexpr_X <T, v_to_void_t<T().X>>
: std::true_type {};
template <class T>
constexpr bool has_constexpr_X_v
= has_constexpr_X<T>::value;
Example uses:
struct HasStaticConstexprX {
static constexpr int X = 2;
};
struct HasStaticConstX {
static const int X; // implied constexpr
};
const int HasStaticConstX::X = 3;
struct HasStaticX {
static int X;
};
int HasStaticX::X = 4;
struct HasConstX {
const int X;
};
int main () {
static_assert(has_constexpr_X_v<HasStaticConstexprX>);
static_assert(has_constexpr_X_v<HasStaticConstX>);
static_assert(! has_constexpr_X_v<HasStaticX>);
static_assert(! has_constexpr_X_v<HasConstX>);
}
Demos:
c++17
c++14

Related

C++ template to check if input type implements `operator []`

I'm trying to use template to check if input type implements operator[]. Here is my code:
#include <iostream>
#include <vector>
using namespace std;
template <typename T, typename U = void>
struct has_bracket
{
static constexpr int value = 0;
};
template <typename T>
struct has_bracket<T, decltype(T::operator[])>
{
static constexpr int value = 1;
};
But it didn't work. It always output 0 no matter which type I input.
struct Test
{
int operator[](size_t n) { return 0; }
};
struct CTest
{
int operator[](size_t n) const { return 0; }
};
int main()
{
cout << has_bracket<int>::value << endl; // output: 0
cout << has_bracket<double>::value << endl; // output: 0
cout << has_bracket<Test>::value << endl; // output: 0
cout << has_bracket<CTest>::value << endl; // output: 0
cout << has_bracket<vector<int>>::value << endl; // output: 0
return 0;
}
I think that if T = int or T = double, decltype(&T::operator[]) will fail and the primary has_bracket will be used according to SFINAE. If T = Test or T = CTest or T = vector<int>, the specialization one will be instantiated, leads to the has_bracket<T>::value be 1.
Is there something wrong? How to fix this problem to let has_bracket<T> be 1 for T = Test, CTest and vector<int>?
Thats not how SFINAE works. has_bracket<int> does not explicitly specify second template argument, default is void, hence it is has_bracket<int,void>.
decltype(T::operator[]) is never void. Hence, you always get the primary template. Moreover decltype(T::operator[]) would require operator[] to be a static member.
SFINAE works when the specialisation has void as second argument for the "true" case, because thats the default of the primary template. std::void_t can be handy to have a type that is either void or a substitution failure:
template <typename T, typename U = void>
struct has_bracket
{
static constexpr int value = 0;
};
template <typename T>
struct has_bracket<T, std::void_t<decltype(std::declval<T>()[std::size_t{1}])> >
{
static constexpr int value = 1;
};
Complete Demo
If you are resitricted to < C++17, ie you cannot use std::void_t you can replace it with a handwritten (taken from cppreference):
template< typename... Ts >
struct make_void { typedef void type; };
 
template< typename... Ts >
using void_t = typename make_void<Ts...>::type;
Summarizing from comments:
The main issue with your code is that the second argument of the specialization is not void, hence it is never choosen.
Your code uses decltype(T::operator[]) and irrespective of the first bullet, this requires the operator to be static (see here)
The issue with decltype(&T::operator[]) is that you cannot take the address when there is more than one overload (see here)
The previous solution in this answer uses decltype(std::declval<T>()[0]) which requires that operator[] can be called with 0 as argument. That this "works" with std::map<std::string,int>::operator[] is an unfortunate coincidence, because a null pointer can be converted to std::string. It does not work when operator[] takes an argument that cannot be constructed from 0 (see here).
For this reason I changed the code above to decltype(std::declval<T>()[std::size_t{1}]). The literal 1 does not have the problem of implicit conversion to a null pointer. The solution now only detects operator[] that can be called with an integer.

Expansion of multiple parameter packs of types and integer values

I previously asked this question, which basically asked how do I change the following "pseudo code" to get the result the comments show:
struct MyStructure {
std::array<pair<TYPE, int>> map {
{ int, 1 },
{ char, 2 },
{ double, 4 },
{ std::string, 8 }
};
template <typename T>
auto operator=(const T &arg) {
// Depending on the type of 'arg', I want to write a certain value to the member variable 'type_'
}
int type_ = 0;
};
int main() {
MyStructure myStruct;
myStruct = 1; // Should cause 1 to be stored in member 'type_ '
myStruct = "Hello world"; // Should cause 8 to be stored in member 'type_'
}
I obtained a suitable answer in the linked question, but now I have a different requirement. I would like to be able to pass the possible integer values and types through as template arguments, and somehow expand them to auto-generate the specialisations as given on the accepted answer. The reason for this is that MyStructure now has to specify the possible values that can be assigned to it, so the 'hard coded' solution approach would not work.
My first thought was to create a new class that MyStructure derives from, that takes the types parameter pack and the ints parameter pack...
template<typename... Types, int...ints>
class Base_Type {
// Somehow expand the packs to generate the specialisations
};
template<typename... Types, int...ints>
struct MyStructure : Base_Type<Types..., ints...> {
//
template <typename T>
auto operator=(const T &arg) {
// Check T is within 'Types'
// Query Base_Type for the T specialization to get the corresponding integer value to write to 'type_'
}
int type_ = 0;
};
Unfortunately I can't see how to do this because - for a start - I can only apparently have one template parameter pack:
'Types': if a class template has a template parameter pack it must appear at the end of the template parameter list
Is what I want to achieve here possible with C++ 17?
It's not possible to generate specializations, but you don't actually need those.
It's not possible to have more than one template parameter pack per class template, so we'll have to work with a single one, with a helper struct that combines both a type and its index into a single type.
#include <cstddef>
#include <iostream>
#include <type_traits>
template <typename T, int I>
struct Type
{
using type = T;
static constexpr int value = I;
};
template <typename ...P>
struct Foo
{
int index = 0;
template <typename T, std::enable_if_t<(std::is_same_v<T, typename P::type> || ...), std::nullptr_t> = nullptr>
Foo &operator=(const T &)
{
(void)((std::is_same_v<T, typename P::type> ? (index = P::value, true) : false) || ...);
return *this;
}
};
int main()
{
Foo<Type<int, 10>, Type<float, 20>> x;
x = 42;
std::cout << x.index << '\n'; // 10
x = 42.f;
std::cout << x.index << '\n'; // 20
// x = 42L; // Error.
}
(X || ...) is a fold expression. It repeats X multiple times, once per element of P. Every use of P in X is replaced with its i-th element.
We use it as a poor man's loop. When the types match and ? : returns true, the loop stops.
The cast to (void) silences the "unused result" warning.

SFINAE to Selectively Include Member

I am trying to write a template class which may or may not define a particular member function depending on its template parameter type. Further the return type of this member function depends on the return type of of a member of the template paramter (if defined).
Below is a minimal example of my code
#include <iostream>
#include <type_traits>
template <typename T>
struct has_foo_int {
private:
template <typename U>
static decltype(std::declval<U>().foo(0), void(), std::true_type()) test(int);
template <typename>
static std::false_type test(...);
public:
typedef decltype(test<T>(0)) test_type;
enum { value = test_type::value };
};
template <typename T, bool HasFooInt>
struct foo_int_return_type;
template<typename T>
struct foo_int_return_type<T,false> {};
template<typename T>
struct foo_int_return_type<T,true> {
using type = decltype(std::declval<T>().foo(0));
};
template<typename T>
struct mystruct
{
T val;
//auto someMethod(int i) -> decltype(std::declval<T>().foo(0)) // error: request for member ‘foo’ in ‘std::declval<double>()’, which is of non-class type ‘double’
//auto someMethod(int i) -> typename foo_int_return_type<T,has_foo_int<T>::value>::type // error: no type named ‘type’ in ‘struct foo_int_return_type<double, false>’
template<typename R=typename foo_int_return_type<T,has_foo_int<T>::value>::type> R someMethod(int i) // error: no type named ‘type’ in ‘struct foo_int_return_type<double, false>’
{
return val.foo(i);
}
};
struct with_foo_int {
int foo(int i){
return i+1;
}
};
using namespace std;
int main(void)
{
mystruct<with_foo_int> ms1;
cout << ms1.someMethod(41) << endl;
mystruct<double> ms2;
return 0;
}
What I would like to happen is that the code compiles fine and outputs 42 for ms1.someFunc(41). I would also expect that if one accidentally tried to call someFunc on ms2 that it would fail to compile.
Unfortunately each of the alternatives I have tried has failed. The first and second, I think I understand why they wouldn't work.
I read here that SFINAE only works for template functions so I tried giving a dummy template parameter to work out the return type but this too fails in the same way.
I'm clearly not understanding something here, what am I missing? Is it possible to achieve what I'm trying to do?
Thanks.
P.s. I'm using g++ 4.7.3
P.p.s I have also tried std::enable_if but get much the same results as with my foo_int_return_type struct.
Here is a short, tidy and documented way of doing what you are attempting,
with some possible bugs addressed thereafter.
#include <type_traits>
/*
Template `has_mf_foo_accepts_int_returns_int<T>`
has a static boolean public member `value` that == true
if and only if `T` is a class type that has a public
member function or member function overload
`int T::foo(ArgType) [const]` where `ArgType`
is a type to which `int` is implicitly convertible.
*/
template <typename T>
struct has_mf_foo_accepts_int_returns_int {
/* SFINAE success:
We know now here `int *` is convertible to
"pointer to return-type of T::foo(0)"
*/
template<typename A>
static constexpr bool test(
decltype(std::declval<A>().foo(0)) *prt) {
/* Yes, but is the return-type of `T::foo(0)`
actually *the same* as `int`?...
*/
return std::is_same<int *,decltype(prt)>::value;
}
// SFINAE failure :(
template <typename A>
static constexpr bool test(...) {
return false;
}
/* SFINAE probe.
Can we convert `(int *)nullptr to
"pointer to the return type of T::foo(0)"?
*/
static const bool value = test<T>(static_cast<int *>(nullptr));
};
template<typename T>
struct mystruct
{
using has_good_foo = has_mf_foo_accepts_int_returns_int<T>;
T val;
/* SFINAE:
`template<typename R> R someMethod(R)` will be this if and only
if `R` == `int` and `has_good_foo` == true.
*/
template<typename R = int>
typename std::enable_if<
(has_good_foo::value && std::is_same<R,int>::value),R
>::type
someMethod(R i) {
return val.foo(i);
}
/* SFINAE:
`template<typename R> R someMethod(R)` will be this if and only
if `R` != `int` or `has_good_foo` != true.
*/
template<typename R = int>
typename std::enable_if<
!(has_good_foo::value && std::is_same<R,int>::value),R
>::type
someMethod(R i) {
static_assert(has_good_foo::value && std::is_same<R,int>::value,
"mystruct<T> does not implement someMethod(R)");
return i;
}
};
// Testing...
#include <iostream>
struct with_foo_int
{
int foo(int i) {
return i + 1;
}
};
using namespace std;
int main(void)
{
mystruct<with_foo_int> ms1;
cout << ms1.someMethod(41) << endl;
mystruct<double> ms2;
cout << ms2.someMethod(41) << endl; // static_assert failure
return 0;
}
This solution faithfully reproduces a couple of possible loopholes in your
own attempt as posted:-
1) It looks as if you may believe that evaluating std::declval<U>().foo(0) is
a SFINAE way of determining whether U::foo exists and takes a single argument
of type int. It doesn't. It is merely a SFINAE way of determining whether
U::foo(ArgType) exists where ArgType is anything to which 0 is
implicitly convertible. Thus ArgType could be any pointer-or-arithmetic
type, not just int.
2) You may not have considered that std::declval<U>().foo(0) will be satisfied
if either or both of U::foo(ArgType) U::foo(ArgType) const exists. You
may well care whether you call a const or a non-const member function on
U, and you would certainly care which of two member function you call. If
with_foo_int were defined as:
struct with_foo_int
{
int foo(int i) const {
return i + 1;
}
int foo(int i) {
return i + 2;
}
};
then the solution given would call the non-const overload and
ms1.someMethod(41) would == 43.
2) Is easily dealt with. If you wish to ensure that you can only call
T::foo(ArgType) const then add a const qualifier to mystruct::someMethod.
If you don't care or wish only to call T::foo(ArgType) then leave things
as they are.
1) is a little harder to solve, because you must craft a SNIFAE probe for
T::foo that is satisfied only if it has the right signature, and that
signature will either be const qualified or not. Let's assume you want
int T::foo(int) const. In that case, replace template
has_mf_foo_accepts_int_returns_int with:
/* Template `has_mf_foo_arg_int_returns_int<T>
has a static boolean public member `value` that == true
if and only if `T` is a class type that has an un-overloaded
a public member `int T::foo(int) const`.
*/
template< typename T>
struct has_mf_foo_arg_int_returns_int
{
/* SFINAE foo-has-correct-sig :) */
template<typename A>
static std::true_type test(int (A::*)(int) const) {
return std::true_type();
}
/* SFINAE foo-exists :) */
template <typename A>
static decltype(test(&A::foo))
test(decltype(&A::foo),void *) {
/* foo exists. What about sig? */
typedef decltype(test(&A::foo)) return_type;
return return_type();
}
/* SFINAE game over :( */
template<typename A>
static std::false_type test(...) {
return std::false_type();
}
/* This will be either `std::true_type` or `std::false_type` */
typedef decltype(test<T>(0,0)) type;
static const bool value = type::value; /* Which is it? */
};
and in template mystruct replace:
using has_good_foo = has_mf_foo_accepts_int_returns_int<T>;
with:
using has_good_foo = has_mf_foo_arg_int_returns_int<T>;
(Template has_mf_foo_arg_int_returns_int is adapted
from my other answer and
you can read how it works there.)
What you gain in SFINAE-precision from the latter approach comes at
a price. The approach requires you to attempt to take the address of T::foo,
to see if it exists. But C++ will not give you the address of an overloaded
member function, so this approach will fail if T::foo is overloaded.
The code here will compile (or appropriately static_assert) with
GCC >= 4.7.2 clang >= 3.2.

enable_if + type template, no SFINAE (enable_if_c without boost ?)

I understand from reading various posts that the following is not supposed to compile.
#include <type_traits>
#include <iostream>
template <bool is_constant> struct A {
// Need to fix this for g++-4.7.2
// implicit conversion to int iff is_constant == true statically
template <class = typename std::enable_if<is_constant>::type>
constexpr operator int() const {
return 10;
}
};
int main()
{
A<true> a;
int i = 2 + a;
std::cout << i << "\n";
A<false> b;
// int i = 2 + a; // compilation error
}
Still, clang 3.2 accepts this code version and it runs fine. My understanding is that it uses an internal version of enable_if_c under the hood.
Now I want to have this compile under gcc which does not accept it.
I understand it would be good to have an actual type and use SFINAE as per other posts.
In my case:
I am trying to define an operator, so I cannot fuss around with extra parameters that have some default type/value -> it seems I cannot use SFINAE.
I cannot use inheritance either because I must keep everything constexpr.
I cannot use any boost include in my code (enable_if_c) because of project requirements
Do I have a way out ?
why not to use specialization?
#include <iostream>
template <bool is_constant>
struct A {};
template <>
struct A<true> {
constexpr operator int() const {
return 10;
}
};
int main()
{
A<true> a;
int i = 2 + a;
std::cout << i << "\n";
A<false> b;
// int ii = 2 + b; // compilation error
}
this is quite straight forward and cross-compiler approach...

Why is term not evaluating to a function taking 0 arguments?

When I try compiling
template<bool val>
struct boolean { static const bool value = val; };
template<typename T>
struct is_callable : boolean<sizeof((*(T*)0)()) >= 0> { }; // error!
int main(void) { bool b = is_callable<int (*)()>::value; }
I get:
error C2064: term does not evaluate to a function taking 0 arguments
see reference to class template instantiation 'is_callable<T>' being compiled
I'm pretty sure int (*)() is callable with 0 arguments... so why doesn't this compile?
The problem is not the use of int(). You can completely remove that from the example and get the same error. The problem is the sizeof expression itself when used as a non-type template argument. Example
template<bool val>
struct boolean { };
template<typename T>
struct is_callable : boolean<sizeof((*(T*)0)()) >= 0> // Error
{
void Test()
{
auto x = sizeof((*(T*)0)()) >= 0; // Compiles
}
};
Hopefully another C++ guy can come along and determine if this sizeof expression is simply illegal as a type argument or if this is just a limitation in the MS C++ compiler.
For me this works.
typedef int (*X)();
template<typename T>
struct is_callable : boolean<sizeof((*(X*)(T*)0)()) >= 0> { }; // works!
So, it looks like the compiler is not sure that you will always pass a function pointer to T when you instantiate the template class !. So, force the compiler with an explicit cast.
[Edit] : Also, on further thinking, I don't understand what you are really trying to do. Are you trying to measure the size of a function pointer which takes a single parameter? How this is going to be different for functions with different return types? Why do you need template at all for a constant expression (which is sizeof(void*))?
Please check this thread for more understanding
What is guaranteed about the size of a function pointer?
You could just use simple template specialization.
#include <stdio.h>
template<typename T>
struct func_with_zero_args { static const bool value = false; };
template<>
struct func_with_zero_args <int (*)()> { static const bool value = true; };
#define STRINGIFY(t) "" #t
#define TEST(t) printf(STRINGIFY(t) ": %s\n", (func_with_zero_args<t>::value ? "yes" : "no"));
int
main(int argc, const char* argv[])
{
TEST(void);
TEST(void (*)(void));
TEST(void (*)(int));
TEST(int (*)(void));
TEST(int (*)(int));
return 0;
}
Generates (using g++ (Ubuntu/Linaro 4.7.3-1ubuntu1) 4.7.3)
void: no
void (*)(void): no
void (*)(int): no
int (*)(void): yes
int (*)(int): no