使自定义类型的行为类似于 std::string

问题描述 投票:0回答:1

我必须定义一些包含

std::string
类型成员的结构。我需要让他们表现得像
std::string
。换句话说,它们应该可以轻松地转换为
std::string
,但不能相互转换。这些结构的实例又将成为另一个类的成员:

class Foo
{
    Name_t m_name;
    Description_t m_description;
// ...
};

因此,采用

Name_t
类型参数的函数不应使用
Description_t
实例进行调用,即使它们都是
std::string
在幕后。

如何以适当的方式实现这一目标?我尝试为所有这些类型重载

operator std::string&
。不过这似乎有效。我不确定是否应该使用
explicit
关键字来声明它们。

一个例子:

#include <type_traits>
#include <utility>
#include <string>
#include <cassert>


struct Description_t
{
    std::string value;

    operator std::string&( ) noexcept
    {
        return value;
    }

    operator const std::string&( ) const noexcept
    {
        return value;
    }
};

bool operator==( const Description_t& lhs, const Description_t& rhs ) noexcept
{
    // This using declaration looks too verbose to my eyes!
    using underlying_type = std::add_lvalue_reference_t<
                              std::add_const_t<
                                decltype( std::declval<decltype( lhs )>( ).value )> >;

    return static_cast<underlying_type>( lhs ) == static_cast<underlying_type>( rhs );
    // return lhs.value == rhs.value; // equivalent to the above statement
}

struct Name_t
{
    std::string value;

    operator std::string&( ) noexcept
    {
        return value;
    }

    operator const std::string&( ) const noexcept
    {
        return value;
    }
};

bool operator==( const Name_t& lhs, const Name_t& rhs ) noexcept
{
    // Again, this using declaration looks too verbose to my eyes!
    using underlying_type = std::add_lvalue_reference_t<
                              std::add_const_t<
                                decltype( std::declval<decltype( lhs )>( ).value )> >;

    return static_cast<underlying_type>( lhs ) == static_cast<underlying_type>( rhs );
    // return lhs.value == rhs.value; // equivalent to the above statement
}

int main()
{
    Description_t stock1 { "Hi!" };
    Description_t stock2 { "Hi!" };
    Name_t name1 { "Hello!" };
    Name_t name2 { "Hello!" };

    assert( name1 == name2 );
    // assert( name1 == stock1 );
}

但是,在

underlying_type
的定义中,我必须通过添加
ref
const
来对 static_cast 进行 const std::string& 操作。否则,编译器将在
operator==
内生成两个低效的复制结构来比较两个
value
参数的
Name_t
。有没有更简洁的方法来做到这一点(除了
lhs.value == rhs.value
)?

在上述情况下,

operator==
也应该是成员函数吗?

c++ operator-overloading stdstring c++23 value-categories
1个回答
0
投票

如果您想要一个行为类似于字符串的类型化字符串,带有 std::string 继承的 CRTP 将是一个选项:

template <typename>
struct TypeSafeString : std::string {
  using std::string::string;
};

struct Description : TypeSafeString<Description> {
  using TypeSafeString<Description>::TypeSafeString;
};

struct Name : TypeSafeString<Name> {
  using TypeSafeString<Name>::TypeSafeString;
};

A

std::string
始终会转换为
Description
Name
,但
Description
不会转换为
Name
std::string
(反之亦然)。您还可以避免 CRTP 并通过
using
定义类型,但随后您必须为每种类型提供有保证的不同模板参数,这可能会出现一些问题。

© www.soinside.com 2019 - 2024. All rights reserved.