3

As I know how to pass template function as template argument, I'm now struggling to pass variable template in a similar manner.

Here is minimal example of what I tried:

#define PASS_VARIABLE_TEMPLATE(name) [dummy=nullptr](auto&&...args) \
                                                    {return name<decltype(args)...>;}

//testing
template <typename T>
bool value = std::is_fundamental<T>::value;

template <typename Hax>
void print_bool(Hax h)
{
    std::cout << h(int{}) << std::endl; // no error, wrong output
    //std::cout << h(int{}, float{}) << std::endl; // error, good
}

int main()
{
    print_bool(PASS_VARIABLE_TEMPLATE(value)); //prints 0 instead of 1
}

Demo

If it compiles, then why the output is wrong?

Community
  • 1
  • 1
xinaiz
  • 7,744
  • 6
  • 34
  • 78
  • Is a reference good enough? – lorro Oct 09 '16 at 15:04
  • @lorro Oh silly me, forgot to do the decaying! But on other hand that would destroy reference/pointer checking... It should be possible to check this case too: `int b; int& ref = a; h(ref); //type is int&` – xinaiz Oct 09 '16 at 15:05
  • You will get the expected results with `#define PASS_VARIABLE_TEMPLATE(name) [dummy=nullptr](auto&&args) { return name::type>; }` – Sam Varshavchik Oct 09 '16 at 15:12
  • @SamVarshavchik Yes, but on the other hand if `std::is_rvalue_reference` was applied instead, wouldn't it break? – xinaiz Oct 09 '16 at 15:15

2 Answers2

3

The main problem with your code is that decltype deduces the arguments as an rvalue reference (int&&) because your lambda uses forwarding references to accept the arguments. std::is_fundamental will work well with a bare type.

For your specific snippet, the correct solution is to remove the reference.

#define PASS_VARIABLE_TEMPLATE(name) \
    [dummy=nullptr](auto&&...args){return name<std::remove_reference_t<decltype(args)>...>;}

Now it works. :-) See it Live On Coliru


A slightly more or better generic way will be to additionally remove cv qualifiers. In the end, you may want to use std::decay

#define PASS_VARIABLE_TEMPLATE(name) [dummy=nullptr](auto&&...args) \
{return name<std::decay_t<decltype(args)>...>;}
WhiZTiM
  • 21,207
  • 4
  • 43
  • 68
1
template<class T>struct tag_t{using type=T; constexpr tag_t(){}};
template<class Tag>using tagged_type=typename Tag::type;
template<class T>constexpr tag_t<T> tag{};

These help pass types as values and unpack them.

#define PASS_VARIABLE_TEMPLATE(name) [](auto...args) \
                                                {return name<tagged_type<decltype(args)>...>;}

Inside print_bool you do:

std::cout << h(tag<int>) << std::endl;

Not sure why you do the dummy=nullptr thing.

tag as a template can carry types unmolested.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • 3
    `dummy` is due to GCC bug with non-capturing lambda (check out the accepted answer from link I posted in the question). Btw, great solution. – xinaiz Oct 09 '16 at 15:22