1

I'm trying to get one set of behavior when something is a pod, and something else when it's not through template meta programming. I've written the below code, but I get a compilation error. I want to get:

yep
nope

but I get the following compiler error:

error C2993: 'std::is_pod<_Ty>': illegal type for non-type template parameter '__formal'

Using this code

#include <iostream>
#include <type_traits>

struct A
{
    int b;
};

struct B
{
private:
    int b;
public:
    int c;
};

template <class Z, std::is_pod<Z>>
void x()
{
    std::cout << "yep" << std::endl;
}

template <class Z>
void x()
{
    std::cout << "nope" << std::endl;
}

int main()
{
    x<A>();
    x<B>();
    return 0;
}

Any advice?

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
Carbon
  • 3,828
  • 3
  • 24
  • 51

4 Answers4

4

You need to use std::enable_if to use the value from std::is_pod in a SFINAE context. That would look like

// only enable this template if Z is a pod type
template <class Z, std::enable_if_t<std::is_pod_v<Z>, bool> = true>
void x()
{
    std::cout << "yep" << std::endl;
}

// only enable this template if Z is not a pod type
template <class Z, std::enable_if_t<!std::is_pod_v<Z>, bool> = true>
void x()
{
    std::cout << "nope" << std::endl;
}

Do note that std::is_pod is deprecated in C++17 and has been removed from C++20.

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
  • Um, `template` ... I don't think that works? – Yakk - Adam Nevraumont Sep 28 '18 at 15:46
  • @Yakk-AdamNevraumont Yeah. It's fixed now. – NathanOliver Sep 28 '18 at 15:50
  • @Yakk-AdamNevraumont It's working for me with both [clang](https://wandbox.org/permlink/D2207vPYoFAY4T0J) and [gcc](https://wandbox.org/permlink/YllJH3VKvdDBSNPt) (sorry, I had the wrong code in the previous links) – NathanOliver Sep 28 '18 at 16:06
  • 1
    @NathanOliver: I think that is from the reading of *"[Non-type template parameter] is one of the following types [..] a pointer type (to object or to function)"* which might exclude *pointer to void*. Another concern was allowed conversions which might not include the one required from `nullptr` to `void*`. – Jarod42 Sep 28 '18 at 16:39
  • @Jarod42 I'm not sure. It is one of the use cases shown [here](https://en.cppreference.com/w/cpp/types/enable_if) and I and others have used this technique for years. Yakk himself has: https://stackoverflow.com/a/31524848/4342498 – NathanOliver Sep 28 '18 at 16:46
  • 1
    I used that too, but I think that pedantically, it was not allowed. maybe it is just `void* = nullptr`and not `void* = 0`. Notice that some examples use `int = 0`. – Jarod42 Sep 28 '18 at 16:54
  • @NathanOliver *nod*, I've seen compilers refuse `void* =nullptr` or `void* =0`, and had to adapt to avoid it to be portable. The error may have been something about `void` is not an object. So I started using `enable_if_t< cond, bool> =true`. I don't know *which* compiler. – Yakk - Adam Nevraumont Sep 28 '18 at 17:24
  • @Yakk-AdamNevraumont Sounds good. I think I'll change to that myself as I like how it looks. I've updated the answer to use a `bool` instead of `void*`. – NathanOliver Sep 28 '18 at 18:05
  • @NathanOliver Ya, it looks way better than it actually is, as the `=true` has nothing to do with truth. ;) But as a cargo cult pattern it works. – Yakk - Adam Nevraumont Sep 28 '18 at 18:30
  • @Yakk-AdamNevraumont I know this is late but I saw [this](https://stackoverflow.com/questions/50765742/why-void-0-and-void-nullptr-makes-the-difference) today which at least explains why `void* =0` doesn't work. – NathanOliver Jan 08 '19 at 17:34
2

With c++17, you might use if constexpr (even if simple if is enough in your case as both branches are valid)

template <class Z>
void x()
{
    if constexpr (std::is_pod_v<Z>) {
        std::cout << "yep" << std::endl;
    } else {
        std::cout << "nope" << std::endl;
    }
}
Wyck
  • 10,311
  • 6
  • 39
  • 60
Jarod42
  • 203,559
  • 14
  • 181
  • 302
1
template <class Z,
  std::enable_if_t<std::is_pod<Z>{}, bool> =true
>
void x()
{
  std::cout << "yep" << std::endl;
}

this conditionally creates a non-type template parameter of type bool, and assigns it true.

If is_pod<Z>{} is false, it generates a SFINAE failure.

You'll have to implement the inverse condition in the other x.

An alternative is tag dispatching:

namespace impl {
  template <class Z>
  void x(std::true_type /* is pod */ )
  {
    std::cout << "yep" << std::endl;
  }

  template <class Z>
  void x(std::false_type /* is pod */ )
  {
    std::cout << "nope" << std::endl;
  }
}
template<class Z>
void x() {
  impl::x<Z>( std::is_pod<Z>{} );
}

where we use usual overload resolution to dispatch between the two bodies. I, personally, find this the most sane.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
1

If you can use C++20, here's a solution that uses requires.

template <class Z>
requires std::is_standard_layout_v<Z> && std::is_trivial_v<Z>
void x()
{
    std::cout << "yep" << std::endl;
}
Steve Ward
  • 486
  • 4
  • 11