Are there any simple types sizeof(which) > 1 is guaranteed?

Viewed 174

When writing generic code, I often want a simple type T for which sizeof(T) > 1 is guaranteed.

For example:

template <typename T>
char test_foo(...);
template <typename T,
    typename = std::enable_if_t<std::is_member_function_pointer_v<decltype(&T::foo)>>>
??? test_foo(int);
template <typename T>
struct has_foo : std::bool_constant<sizeof(test_foo<T>(0))!=1> {};

I have a few options, but none is ideal:

  • long long: does not work, see here
  • struct SizeNot1 { char dummy[2]; };: have to define it ahead, a little bit annoying
  • char (&test_foo())[2];: too ugly
  • std::array<char, 2>: is its size guaranteed?

Any ideas?

4 Answers

When writing generic code, I often want a simple type T for which sizeof(T) > 1 is guaranteed.

I think you do it the "wrong" way.

For your example, you might directly use std::true_type/std::false_type.

template <typename>
std::false_type test_foo(...);

template <typename T, std::enable_if_t<std::is_member_function_pointer_v<decltype(&T::foo)>, int> = 0>
std::true_type test_foo(int);

template <typename T>
using has_foo = deltype(test_foo<T>(0));

If you need different compile_time number inside a type, std::integral_constant might help:

template <typename>
std::integral_constant<std::size_t, 0> test_foo(...);

template <typename T, std::enable_if_t<std::is_member_function_pointer_v<decltype(&T::foo)>, int> = 0>
std::integral_constant<std::size_t, 42> test_foo(int);

template <typename T>
using has_foo = std::bool_constant<deltype(test_foo<T>(0))() == 42>;

char (&test_foo())[2];: too ugly

There are alternative syntaxes which are nicer:

auto test_foo() -> char(&)[2];

but using typedef would be even more readable IMO:

template <std::size_t N>
using c_char_array = char[N];

and then

c_char_array<2>& test_foo();

It just comes to my head that this should work:

std::aligned_storage_t<2> & test_foo(int);

This does not introduce new names and is fairly readable.

Since sizeof(char) == 1, you can use a char[2]. As you've mentioned, the syntax for that is

char (&test_foo())[2];

which works, but may be a bit difficult to read.

It's a subjective answer, but I think

decltype(" ") test_foo();

is fairly readable. Here we're just using the type of a literal of type char[2].

Just in case. If you trying to do template which detects presence of function, this how it can be done:

#include <iostream>
#include <type_traits>

class A {
public:
    int func() {return 1;}
};

class B {
};

template <typename T, typename = void>
struct has_func : std::false_type {};

template <typename T>
struct has_func<T, std::void_t<decltype(std::declval<T>().func())>>
    : std::true_type {};

template<typename T>
int wrap(T &t) {
    if constexpr (has_func<T>::value) {
        return t.func();
    } else {
        return 0;
    }
}


int main()
{
    A a;
    B b;
    std::cout << "A:" << wrap(a) << ", B:" << wrap(b) << "\n";
}
Related