Showing posts with label cpp_tmp_is_convertible. Show all posts
Showing posts with label cpp_tmp_is_convertible. Show all posts

May 10, 2023

[C++] SFINAE for testing type convertible

Reference:
https://en.cppreference.com/w/cpp/types/is_convertible
[C++][cppcon 2017] Write our own type trait

Use function parameter type to test if a incoming argument type is implicitly convertible to parameter type.

This trick can be used in SFINAE to test type convertibility.

#include <type_traits>

struct O2 {};
struct O1 {
  O1() = default;
  O1(const O2 &) {}
};

int main() {
  // cannot be converted:
  // decltype(void(std::declval<void (&)(O2)>()(std::declval<O1>()))) A;
  decltype(void(std::declval<void (&)(O1)>()(std::declval<O2>()))) *A = nullptr;
};

Oct 14, 2018

[C++] C++17 RVO is the standard.

With side-project on Envoy, found something interesting thus jot down for future reference.

  • Idiom:
    Functions take values as arguments and std::move to final destination. (Less constructor/destructor calls involved)

    Or use template trick to limit the universal constructor having higher priority then non-template ones.
  • C++17 has RVO into the language. Thus,
    -fno-elide-constructors
    will not turn off RVO in any how.
  • is_convertible, used as the guard for universal constructor to allow copy constructor takes itself type's instance or derived one.
    However, is_convertible isn't using the 'cast' as implementation, which is in run-time, it simply uses a form like:
    To test() { return std::declval<From>(); } // ODR use of declval here, which static_assert fails. It's just an example.
    That is to say, any type with conversion operator would meet the is_convertible requirement, not only the derived ones.
  • explicit is your friend. Single parameter constructor, including template ones, use explicit prevent automatic type conversion by accident.

As usual, one example explains what mentioned above.
#include <boost/type_index.hpp>
#include <iostream>
#include <string_view>
#include <utility>

using namespace std;


struct Base {
    // -- 1
    // Guarded , using enable_if_t, is_convertible_v for C++17
    template <typename T, typename = typename enable_if<
                              !is_convertible<T, Base>::value>::type>
    explicit Base(T &&t)
    {
    }

    Base(const Base &) {}  // on purpose, compiler generates by default -- 2

    Base(Base &&) {}  // on purpose, compiler generates by default. -- 3
};

struct Derived : Base {
    using Base::Base;
};

struct Mutation {
    explicit operator Base()
    {
        return Base{42}; // Hits (1)
    }
};

int main()
{
    auto _ = "Pikachu"sv;

    Mutation m;
    // static_cast<Base>(m);  // Can't call this way due to Base's universal
    // constructor installed.
    Base b1{
        m.operator Base()}; // -fno-elide-constructors has no effect for C++17
    // By default Move constructor will never be hit in C++17
    // Prior then C++17, Move constructor will be hit if no -fno-elide-constructors
    Base b2{D{42}}; // Hits (3), no RVO, RVO only happens on _same_ type.
}