【C++11】区分左值、亡值、纯右值,及他们的引用

一、cppreference和stackoverflow解释

在这里插入图片描述

仅说一点:右值也可以放到等号左边

#include <iostream>
 
struct S
{
    S() : m{42} {}
    S(int a) : m{a} {}
    int m;
};
 
int main()
{
    S s;
 
    // 表达式 `S{}` 是纯右值
    // 可以出现在赋值表达式的右侧
    s = S{};
 
    std::cout << s.m << '\n';
 
    // 表达式 `S{}` 是纯右值
    // 也可以在左侧使用
    std::cout << (S{} = S{7}).m << '\n';
}

二、获取变量的类型cppdemangle.h

cppdemangle.h

#pragma once

#include <typeinfo>
#include <type_traits>
#include <string>
#if (defined(__GNUC__) || defined(__clang__)) && __has_include(<cxxabi.h>)
#include <cxxabi.h>
#include <cstdlib>
#endif

namespace _cppdemangle_details {

static std::string cppdemangle(const char *name) {
#if (defined(__GNUC__) || defined(__clang__)) && __has_include(<cxxabi.h>)
    int status;
    char *p = abi::__cxa_demangle(name, 0, 0, &status);
    std::string s = p ? p : name;
    std::free(p);
#else
    std::string s = name;
#endif
    return s;
}

static std::string cppdemangle(std::type_info const &type) {
    return cppdemangle(type.name());
}

template <class T>
static std::string cppdemangle() {
    std::string s{cppdemangle(typeid(std::remove_cv_t<std::remove_reference_t<T>>))};
    if (std::is_const_v<std::remove_reference_t<T>>)
        s += " const";
    if (std::is_volatile_v<std::remove_reference_t<T>>)
        s += " volatile";
    if (std::is_lvalue_reference_v<T>)
        s += " &";
    if (std::is_rvalue_reference_v<T>)
        s += " &&";
    return s;
}

}

using _cppdemangle_details::cppdemangle;

// Usage:
//
// cppdemangle<int>()
// => "int"
//
// int i;
// cppdemangle<decltype(i)>()
// => "int"
//
// int i;
// cppdemangle<decltype(std::as_const(i))>()
// => "int const &"

print.h

#include <type_traits>
#include <iostream>
#include <iomanip>
#include <string>
#include <string_view>
#include <optional>
#include <variant>

namespace _print_details {
    template <class T, class = void>
    struct _printer {
        static void print(T const &t) {
            std::cout << t;
        }

        using is_default_print = std::true_type;
    };

    template <class T, class = void>
    struct _is_default_printable : std::false_type {
    };

    template <class T>
    struct _is_default_printable<T, std::void_t<std::pair<typename _printer<T>::is_default_print, decltype(std::declval<std::ostream &>() << std::declval<T const &>())>>> : std::true_type {
    };

    template <class T, class = void>
    struct _is_printer_printable : std::true_type {
    };

    template <class T>
    struct _is_printer_printable<T, std::void_t<typename _printer<T>::is_default_print>> : std::false_type {
    };

    template <class T, class = void>
    struct is_printable : std::disjunction<_is_default_printable<T>, _is_printer_printable<T>> {
    };

    template <class T>
    using _rmcvref_t = std::remove_cv_t<std::remove_reference_t<T>>;

    template <class T, class U = void, class = void>
    struct _enable_if_tuple {
        using not_type = U;
    };

    template <class T, class U>
    struct _enable_if_tuple<T, U, std::void_t<decltype(std::tuple_size<T>::value)>> {
        using type = U;
    };

    template <class T, class U = void, class = void>
    struct _enable_if_map {
        using not_type = U;
    };

    template <class T, class U>
    struct _enable_if_map<T, U, std::enable_if_t<std::is_same_v<typename T::value_type, std::pair<typename T::key_type const, typename T::mapped_type>>>> {
        using type = U;
    };

    template <class T, class U = void, class = void>
    struct _enable_if_iterable {
        using not_type = U;
    };

    template <class T, class U>
    struct _enable_if_iterable<T, U, std::void_t<typename std::iterator_traits<decltype(std::begin(std::declval<T const &>()))>::value_type>> {
        using type = U;
    };

    template <class T, class U = void, class = void>
    struct _enable_if_has_print {
        using not_type = U;
    };

    template <class T, class U>
    struct _enable_if_has_print<T, U, std::void_t<decltype(std::declval<T const &>().do_print())>> {
        using type = U;
    };

    /* template <class T, class U> */
    /* struct _enable_if_iterable<T, U, std::void_t<typename std::iterator_traits<typename T::const_iterator>::value_type>> { */
    /*     using type = U; */
    /* }; */

    template <class T>
    struct _is_char : std::false_type {
    };

    template <>
    struct _is_char<char> : std::true_type {
    };

    template <>
    struct _is_char<wchar_t> : std::true_type {
    };

    template <class T, class U = void, class = void>
    struct _enable_if_char {
        using not_type = U;
    };

    template <class T, class U>
    struct _enable_if_char<T, U, std::enable_if_t<_is_char<T>::value>> {
        using type = U;
    };

    template <class T, class U = void, class = void>
    struct _enable_if_string {
        using not_type = U;
    };

    template <class T, class Alloc, class Traits, class U>
    struct _enable_if_string<std::basic_string<T, Alloc, Traits>, U, typename _enable_if_char<T>::type> {
        using type = U;
    };

    template <class T, class Traits, class U>
    struct _enable_if_string<std::basic_string_view<T, Traits>, U, typename _enable_if_char<T>::type> {
        using type = U;
    };

    template <class T, class U = void, class = void>
    struct _enable_if_c_str {
        using not_type = U;
    };

    template <class T, class U>
    struct _enable_if_c_str<T, U, std::enable_if_t<std::is_pointer_v<std::decay_t<T>> && _is_char<std::remove_const_t<std::remove_pointer_t<std::decay_t<T>>>>::value>> {
        using type = U;
    };

    template <class T, class U = void, class = void>
    struct _enable_if_optional {
        using not_type = U;
    };

    template <class T, class U>
    struct _enable_if_optional<std::optional<T>, U, void> {
        using type = U;
    };

    template <class T, class U = void, class = void>
    struct _enable_if_variant {
        using not_type = U;
    };

    template <class ...Ts, class U>
    struct _enable_if_variant<std::variant<Ts...>, U, void> {
        using type = U;
    };

    template <class T>
    struct _printer<T, typename _enable_if_has_print<T>::type> {
        static void print(T const &t) {
            t.do_print();
        }
    };

    template <class T>
    struct _printer<T, typename _enable_if_has_print<T, typename _enable_if_iterable<T, typename _enable_if_c_str<T, typename _enable_if_string<T, typename _enable_if_map<T>::not_type>::not_type>::not_type>::type>::not_type> {
        static void print(T const &t) {
            std::cout << "{";
            bool once = false;
            for (auto const &v: t) {
                if (once) {
                    std::cout << ", ";
                } else {
                    once = true;
                }
                _printer<_rmcvref_t<decltype(v)>>::print(v);
            }
            std::cout << "}";
        }
    };

    template <class T>
    struct _printer<T, typename _enable_if_has_print<T, typename _enable_if_tuple<T, typename _enable_if_iterable<T>::not_type>::type>::not_type> {
        template <std::size_t ...Is>
        static void _unrolled_print(T const &t, std::index_sequence<Is...>) {
            std::cout << "{";
            ((_printer<_rmcvref_t<std::tuple_element_t<Is, T>>>::print(std::get<Is>(t)), std::cout << ", "), ...);
            if constexpr (sizeof...(Is) != 0) _printer<_rmcvref_t<std::tuple_element_t<sizeof...(Is), T>>>::print(std::get<sizeof...(Is)>(t));
            std::cout << "}";
        }

        static void print(T const &t) {
            _unrolled_print(t, std::make_index_sequence<std::max(static_cast<std::size_t>(1), std::tuple_size_v<T>) - 1>{});
        }
    };

    template <class T>
    struct _printer<T, typename _enable_if_has_print<T, typename _enable_if_map<T>::type>::not_type> {
        static void print(T const &t) {
            std::cout << "{";
            bool once = false;
            for (auto const &[k, v]: t) {
                if (once) {
                    std::cout << ", ";
                } else {
                    once = true;
                }
                _printer<_rmcvref_t<decltype(k)>>::print(k);
                std::cout << ": ";
                _printer<_rmcvref_t<decltype(v)>>::print(v);
            }
            std::cout << "}";
        }
    };

    template <class T>
    struct _printer<T, typename _enable_if_has_print<T, typename _enable_if_string<T>::type>::not_type> {
        static void print(T const &t) {
            std::cout << std::quoted(t);
        }
    };

    template <class T>
    struct _printer<T, typename _enable_if_c_str<T>::type> {
        static void print(T const &t) {
            std::cout << t;
        }
    };

    template <class T>
    struct _printer<T, typename _enable_if_char<T>::type> {
        static void print(T const &t) {
            T s[2] = {t, T('\0')};
            std::cout << std::quoted(s, T('\''));
        }
    };

    template <>
    struct _printer<std::nullptr_t, void> {
        static void print(std::nullptr_t const &) {
            std::cout << "nullptr";
        }
    };

    template <>
    struct _printer<std::nullopt_t, void> {
        static void print(std::nullopt_t const &) {
            std::cout << "nullopt";
        }
    };

    template <>
    struct _printer<std::monostate, void> {
        static void print(std::monostate const &) {
            std::cout << "monostate";
        }
    };

    template <class T>
    struct _printer<T, typename _enable_if_has_print<T, typename _enable_if_optional<T>::type>::not_type> {
        static void print(T const &t) {
            if (t.has_value()) {
                _printer<typename T::value_type>::print(*t);
            } else {
                _printer<std::nullopt_t>::print(std::nullopt);
            }
        }
    };

    template <class T>
    struct _printer<T, typename _enable_if_has_print<T, typename _enable_if_variant<T>::type>::not_type> {
        static void print(T const &t) {
            std::visit([] (auto const &v) {
                _printer<_rmcvref_t<decltype(v)>>::print(v);
            }, t);
        }
    };

    template <>
    struct _printer<bool, void> {
        static void print(bool const &t) {
            if (t) {
                std::cout << "true";
            } else {
                std::cout << "false";
            }
        }
    };

    template <class T0, class ...Ts>
    void print(T0 const &t0, Ts const &...ts) {
        _printer<_rmcvref_t<T0>>::print(t0);
        ((std::cout << " ", _printer<_rmcvref_t<Ts>>::print(ts)), ...);
        std::cout << "\n";
    }

    template <class T0, class ...Ts>
    void printnl(T0 const &t0, Ts const &...ts) {
        _printer<_rmcvref_t<T0>>::print(t0);
        ((std::cout << " ", _printer<_rmcvref_t<Ts>>::print(ts)), ...);
    }

    template <class T, class = void>
    class print_adaptor {
        T const &t;

    public:
        explicit constexpr print_adaptor(T const &t_) : t(t_) {
        }

        friend std::ostream &operator<<(std::ostream &os, print_adaptor const &&self) {
            auto oldflags = os.flags();
            os << "[object 0x" << std::hex << reinterpret_cast<uintptr_t>(
                reinterpret_cast<void const volatile *>(std::addressof(self.t))) << ']';
            os.flags(oldflags);
            return os;
        }
    };

    template <class T>
    class print_adaptor<T, std::enable_if_t<is_printable<T>::value>> {
        T const &t;

    public:
        explicit constexpr print_adaptor(T const &t_) : t(t_) {
        }

        friend std::ostream &operator<<(std::ostream &os, print_adaptor const &&self) {
            printnl(self.t);
            return os;
        }
    };

    template <class T>
    explicit print_adaptor(T const &) -> print_adaptor<T>;
}

using _print_details::print;
using _print_details::printnl;
using _print_details::print_adaptor;
using _print_details::is_printable;

// Usage:
//
// map<string, optional<int>> m = {{"hello", 42}, {"world", nullopt}};
// print(m);  // {"hello": 42, "world": nullopt}


/* // use of the macro below requires #include "ppforeach.h" */
/* #define DEF_PRINT(Class, TmplArgs, ...) \ */
/* template <TmplArgs> \ */
/* struct ::constl::_print_details::_printer<Class, void> { \ */
/*     static void print(Class const &_cls) { \ */
/*         std::cout << "{"; \ */
/*         PP_FOREACH(_PRINTER_PER_MEMBER, std::cout << ", ";, __VA_ARGS__); \ */
/*         std::cout << "}"; \ */
/*     } \ */
/* }; */
/* #define _PRINTER_PER_MEMBER(memb) \ */
/*     std::cout << #memb << ": "; \ */
/*     ::constl::_print_details::_printer<_print_details::_rmcvref_t<decltype(_cls.memb)>>::print(_cls.memb); */
/*  */
/* #define PRINT(x) print(#x " :=", x) */

1、decaltype直接取变量和取变量表达式的区别

decaltype变量,直接取得是变量的类型,和表达式的类型是不一样的。

  • 前者取的是变量的类型
  • 后者取得是表达式的类型,decaltype变量,直接取得是变量的类型,和表达式的区别。表达式是可以赋值的呀

取变量的类型:

使用gun++17编译

int main()
{
    int a = 1;
    std::cout<<_cppdemangle_details::cppdemangle<decltype(a)>()<<std::endl;
}

测试:

Program returned: 0
Program stdout
int

取表达式的类型:

int main()
{
    int a = 1;
    std::cout<<cppdemangle<decltype((a))>()<<std::endl;
}

测试:

Program returned: 0
Program stdout
int &

2.void func(int a)和void func(int&& a)无法构成重载

函数的形参类型int&&和int会冲突,入参无法区分prvalue和xvalue。也就是说纯右值的参数可以用&&这样的函数参数类型去接上述两种类型的参数

/*
void func(int a)
{
    std::cout<<__PRETTY_FUNCTION__<<std::endl;
}
*/

void func(int&& a)
{
    std::cout<<__PRETTY_FUNCTION__<<std::endl;
}

void func(int const& a)
{
    std::cout<<__PRETTY_FUNCTION__<<std::endl;
}



int main()
{
    int a = 1;
    std::cout<<cppdemangle<decltype(1)>()<<std::endl;//prvalue
    std::cout<<cppdemangle<decltype(std::move(a))>()<<std::endl;//xvalue
    std::cout<<cppdemangle<decltype((a))>()<<std::endl;//lvalue
    func(1);//prvalue
    func(std::move(a));//xvalue
    func(a);//lvalue
}

测试

Program returned: 0
Program stdout
int
int &&
int &
void func(int&&)
void func(int&&)
void func(const int&)

3.区分this指针式左值还是右值的方法


#include <iostream>
#include <string>

#include <typeinfo>
#include <type_traits>
#include <string>
#if (defined(__GNUC__) || defined(__clang__)) && __has_include(<cxxabi.h>)
#include <cxxabi.h>
#include <cstdlib>
#endif

namespace _cppdemangle_details {

static std::string cppdemangle(const char *name) {
#if (defined(__GNUC__) || defined(__clang__)) && __has_include(<cxxabi.h>)
    int status;
    char *p = abi::__cxa_demangle(name, 0, 0, &status);
    std::string s = p ? p : name;
    std::free(p);
#else
    std::string s = name;
#endif
    return s;
}

static std::string cppdemangle(std::type_info const &type) {
    return cppdemangle(type.name());
}

template <class T>
static std::string cppdemangle() {
    std::string s{cppdemangle(typeid(std::remove_cv_t<std::remove_reference_t<T>>))};
    if (std::is_const_v<std::remove_reference_t<T>>)
        s += " const";
    if (std::is_volatile_v<std::remove_reference_t<T>>)
        s += " volatile";
    if (std::is_lvalue_reference_v<T>)
        s += " &";
    if (std::is_rvalue_reference_v<T>)
        s += " &&";
    return s;
}

}

using _cppdemangle_details::cppdemangle;


struct S{
    int a;
    int b;

    int getA() const& {
        std::cout<<__PRETTY_FUNCTION__<<std::endl;
        return a;
    }
    int getA() &{
               std::cout<<__PRETTY_FUNCTION__<<std::endl;
        return a;
    }

    int getA() &&{
               std::cout<<__PRETTY_FUNCTION__<<std::endl;
        return a;
    }

    int getA() const &&{
               std::cout<<__PRETTY_FUNCTION__<<std::endl;
        return a;
    }

};


int main()
{
    S().getA();
    S s;
    s.getA();
    const S cs{};
    cs.getA();
    std::move(cs).getA();
}

测试:

Program returned: 0
Program stdout
int S::getA() &&
int S::getA() &
int S::getA() const &
int S::getA() const &&

4.区分this指针左值,右值重载的应用例子

如果不定义右值的this指针的相关函数,给func传入的是右值,最后接的func也是左值参数类型的func

void func(int&& a)
{
    std::cout<<__PRETTY_FUNCTION__<<std::endl;
}

void func(int const& a)
{
    std::cout<<__PRETTY_FUNCTION__<<std::endl;
}

struct S{
    int a;
    int b;

    int const& getA() const {
        std::cout<<__PRETTY_FUNCTION__<<std::endl;
        return a;
    }
    int& getA() {
               std::cout<<__PRETTY_FUNCTION__<<std::endl;
        return a;
    }



};


int main()
{
    func(S().getA());

}

测试:

Program returned: 0
Program stdout
int& S::getA()
void func(const int&)

解决办法1:使用move

int main()
{
    func(std::move(S{}.getA()));

}

解决办法2:重载this指针左值和右值的相关成员函数

入参是一个右值,使用能接收右值的func

void func(int&& a)
{
    std::cout<<__PRETTY_FUNCTION__<<std::endl;
}

void func(int const& a)
{
    std::cout<<__PRETTY_FUNCTION__<<std::endl;
}

struct S{
    int a;
    int b;

    int const& getA() const& {
        std::cout<<__PRETTY_FUNCTION__<<std::endl;
        return a;
    }
    int& getA() &{
               std::cout<<__PRETTY_FUNCTION__<<std::endl;
        return a;
    }

    int&& getA() &&{
               std::cout<<__PRETTY_FUNCTION__<<std::endl;
        return std::move(a);
    }

    int const&&  getA() const &&{
               std::cout<<__PRETTY_FUNCTION__<<std::endl;
        return std::move(a);
    }

};


int main()
{
    func(S().getA());

}

测试:

Program returned: 0
Program stdout
int&& S::getA() &&
void func(int&&)

参考

相关推荐

  1. C++:引用)&引用

    2024-06-14 12:54:03       34 阅读
  2. C++11_引用

    2024-06-14 12:54:03       44 阅读
  3. C++引用引用

    2024-06-14 12:54:03       29 阅读
  4. 引用引用

    2024-06-14 12:54:03       32 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-06-14 12:54:03       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-06-14 12:54:03       100 阅读
  3. 在Django里面运行非项目文件

    2024-06-14 12:54:03       82 阅读
  4. Python语言-面向对象

    2024-06-14 12:54:03       91 阅读

热门阅读

  1. AtCoder Beginner Contest 357 C - Sierpinski carpet

    2024-06-14 12:54:03       51 阅读
  2. Threejs-11、材质文理颜色

    2024-06-14 12:54:03       32 阅读
  3. Flutter知识点

    2024-06-14 12:54:03       32 阅读
  4. 软件设计模式概述

    2024-06-14 12:54:03       27 阅读
  5. Linux之逻辑控制符&&

    2024-06-14 12:54:03       30 阅读
  6. 带你学习Mybatis之执行器Executor

    2024-06-14 12:54:03       27 阅读
  7. 聊聊C/S模式架构的优缺点

    2024-06-14 12:54:03       33 阅读
  8. 面试题(常见)

    2024-06-14 12:54:03       33 阅读
  9. Webrtc支持FFMPEG硬解码之NVIDA(二)

    2024-06-14 12:54:03       35 阅读
  10. 字节一面(年前)测开—飞书

    2024-06-14 12:54:03       30 阅读
  11. 代码详解工厂设计模式【2】

    2024-06-14 12:54:03       29 阅读
  12. NumPy 字节交换

    2024-06-14 12:54:03       27 阅读