Why Is There Not an Std::Is_Struct Type Trait

Why is there not an std::is_struct type trait?


It returns true for both classes and structs. I know that in C++ they are almost the same thing, but I'd like to know why there's not a distinction between them in the type trait.

Unfortunately this is a common misconception in C++. Sometimes it comes from fundamental misunderstanding, but at other times it comes from an ambiguity in English. It can come from inaccurate compiler diagnostics, badly-written books, incorrect SO answers…

You've probably read something like this:

"There is no difference in C++ between a struct and a class except the default visibility of members and bases."

This passage can be interpreted in a sense that is misleading, because the notions of identity and equality are hard to distinguish when using phrases like "no difference".

In fact, C++ has not had structs since 1985. It only has classes.

The kind of types that you declare with the keyword class and the keyword struct are classes. Period. The keyword struct, and the visibility rules that are the default when defining a class using that keyword, were kept only for backward compatibility with C … but that's a syntax thing. It doesn't make the resulting types actually be of a different kind.

The type trait makes no distinction because there literally isn't one to make.

Is it possible to create a trait to answer if a type comes from std?

This seems to work:

#include <functional>
#include <type_traits>
#include <utility>
#include <string>

namespace other { struct S{}; }

namespace my {
template< class Type >
void ref( Type&& ) {}

template< class Type >
auto ref_to( Type&& o )
-> Type&
{ return o; }

template< class Type >
constexpr auto is_std_type()
-> bool
{
using std::is_same;
using std::declval;
return not is_same< void, decltype( ref( ref_to( declval<Type>() ) ) )>::value;
}

struct Blah {};

constexpr bool int_is_std = is_std_type<int>();
constexpr bool blah_is_std = is_std_type<Blah>();
constexpr bool other_is_std = is_std_type<other::S>();
constexpr bool string_is_std = is_std_type<std::string>();
};

#include <iostream>
using namespace std;
auto main()
-> int
{
cout << boolalpha;
cout << "int is std = " << my::int_is_std << "\n";
cout << "blah is std = " << my::blah_is_std << "\n";
cout << "other is std = " << my::other_is_std << "\n";
cout << "string is std = " << my::string_is_std << "\n";
}

I know struct is a class but the code says No

At first, you are right. a struct is just a class under the hood.

Some indications:

1- You can forward declare one of them and then define it with the other one (although some compliers may issue a warning).

class A;     // declaration of A with class
struct A { /* definition of A with struct */ } ;

2- is_class<> returns true for both.

3- There is no is_struct<> in <type_traits>.

4- In most cases, the two keywords are interchangeable. (e.g: in scoped enum).

However, they have important syntactic differences:

struct

  • Grants public access to its members by default.
  • Backward compatible with C if used without C++ features.

class

  • Grants private access to its members by default.
  • Not compatible with C.

Which one to use is a matter of convention and/or convenience.

Generally, I prefer using struct as an aggregate of data (record). And class for other uses.

Now we come to your second question:

Why does the code show two different things?

It does not.

Because is_class<> is not about types but rather it is about type categories. (i.e. is_class<x> checks if the type x is a class or not) whereas is_same<> is about types themselves ((i.e. is_same<x, y> checks if type x is as same as type y or not))

If you change struct in your test code into class, is_same<> would still outputs “No” because A is a type and B is another type even if they both have the same type category which is class.

Based on your code:

A a;  // a is of type A
B b; // b is of type B
// Can we say that a and b of the same type?
// No. And this is what is_same<> checks.

User defined type trait gives an unexpected false type

This did the trick for me:

int main()
{
std::cout << is_string_v<char const[sizeof "Hello"]> << '\n';
std::cout << is_string_v<std::remove_reference_t<decltype("Hello")>> << '\n';
}

Output:

1
1

Looks like decltype added an extra reference. Taken from this answer:

The type denoted by decltype(e) is defined as follows:

  • if e is an unparenthesized id-expression or an unparenthesized class member access, decltype(e) is the type of the entity named by e. If there is no such entity, or if e names a set of overloaded functions, the program is ill-formed;

  • otherwise, if e is an xvalue, decltype(e) is T&&, where T is the type of e;

  • otherwise, if e is an lvalue, decltype(e) is T&, where T is the type of e;

  • otherwise, decltype(e) is the type of e.

§7.1.6.2 [dcl.type.simple]

And string literals are lvalues, so it adds a reference.

Is there any type trait which controls member type(not member variable)

If type_member is public (and not private as in your question), I suppose you can do something like

#include <iostream>

template <typename X>
struct with_type_member
{
template <typename Y = X>
static constexpr bool getValue (int, typename Y::type_member * = nullptr)
{ return true; }

static constexpr bool getValue (long)
{ return false; }

static constexpr bool value { getValue(0) };
};

class A
{
public:
using type_member = void;
};

int main ()
{
std::cout << with_type_member<int>::value << std::endl; // print 0
std::cout << with_type_member<A>::value << std::endl; // print 1
}

Hope this helps.

is there type trait that show if one type might contain value of other type

We can take advantage of the existing language rules here.

For list-initialization, narrowing conversions are ill-formed. We are trying to detect narrowing conversions. Hence, to the void_t!

template <class X, class Y, class = void>
struct can_hold : std::false_type { };

template <class X, class Y>
struct can_hold<X, Y,
void_t<decltype(X{std::declval<Y>()})>>
: std::true_type { };

How to check if a template parameter is a struct/class?

You are looking for std::is_class traits from <type_traits> header. Which

Checks whether T is a non-union class type. Provides the member
constant value which is equal to true, if T is a class type (but not
union). Otherwise, value is equal to false.


For instance, you can static_assert for the template type T like follows:

#include <type_traits> // std::is_class

template<typename T>
struct MyStruct
{
static_assert(std::is_class<T>::value, " T must be struct/class type!");
T t;
};

(See a demo)



c++20 concept Updates

In C++20, one can provide a concept using std::is_class as follows too.

#include <type_traits> // std::is_class

template <class T> // concept
concept is_class = std::is_class<T>::value;

template<is_class T> // use the concept
struct MyStruct
{
T t;
};

(See a demo)

Why structures in c++ don't need constructor?


why we need constructors in class

We don't.

// Perfectly valid
class Foo
{
public:
int x
};

why we cant do constructor overloading in structure...

We can.

// Look, ma!
struct Bar
{
Bar operator+(const Bar& other);
};

I don't know where you heard these claims but it certainly wasn't from trying it out.

A user-defined type declared with the keyword struct is a class.



Related Topics



Leave a reply



Submit