如何避免隐式转换类型

3

更新

如果有人对这个项目感兴趣,GitHub项目在这里。虽然它运行得很好,但我已经将其限制为仅适用于算术类型。

未来的发展将带来更多内容。

谢谢大家。

原始文章

是的,我知道这个问题。请阅读一下以获得良好的介绍和一个聪明但不普遍的解决方案。

我确实关心这个问题,但我也需要将一些基本类型设置为不可转换。这样做将严重限制引入错误的可能性。不仅需要禁止隐式转换的函数调用,还包括变量创建、赋值、比较等。

我相信这在以前已经尝试过并得到解决。我的程序中不能有这个:

  // implicit conversions
  // no warnings 
  {
     char character = 127 ;
     bool truth = 0 ;
     size_t size_ = truth ;
     int integer = false ;
     size_ = integer;
     size_ = character ;
  }

在我的VS 2017(最新版本)中,即使在默认设置下编译,也不会出现警告,即使是在Level 4(也称为/W4)下。同样适用于clang 7等。

到目前为止,我已经开发了这个:

namespace dbj {
namespace util {
    using namespace std;

    template<typename T>
    struct nothing_but final
    {
        static_assert(false == std::is_array_v<T>, 
                  "can not deal with arrays");

        using type = nothing_but;

         // default creation
         nothing_but() : val_(T{}) {}
        // allowed conversion
        nothing_but(T const & t_) : val_(t_) {}
        // allowed assignment
        type & operator = (T const & new_val_)
        {
            val_ = new_val_;
            return *this;
        }

        /*
        explictly ban all attempts to construct from
        any other type but T
        */
        template<
            typename X,
            std::enable_if_t<
            false == std::is_same_v<T, X>
            , int> = 0
        >
            nothing_but(X const & x_) = delete;
        /*
        explictly ban all attempts to assign from
        any other type but T
        */
        template<
            typename X,
            std::enable_if_t<
            false == std::is_same_v<T, X>
            , int> = 0
        >
            type & operator = (X const & new_val_) = delete;

        /* conversion to X is banned */
        template<
        typename X,
        std::enable_if_t<
        false == std::is_same_v<T, X>
        , int> = 0
        >
        operator X & () = delete;

        // conversion to T is allowed, but const stays const
        operator T & () { return val_; }

        // non const value, as other std class types do
        T & data() const { return (T&)val_;  }

    private:
        T val_{};
        // compatibility
        friend bool operator < ( type const & left_, type const & right_)
        {
            return left_.val_ < right_.val_;
        }
    };
  } // util
} // dbj

一些快速测试:
{
using dbj::util::nothing_but;

nothing_but<int> si1 = 42;
si1 = 42;
nothing_but<int> si2 = 13;
//nothing_but<int> si3 = true ; 
//si3 = true;
//nothing_but<int> si4 = '$' ;
//si4 = '$';
//nothing_but<int> si5 = 2.7 ;
//si5 = 2.7;
//nothing_but<int> si6 = size_t(BUFSIZ) ;
//si6 = size_t(BUFSIZ);

si1 = si2; (void)(si1 == si2);

int j = 9;
nothing_but<int *> sip1 = &j;
nothing_but<char const *> scc1 = "OK";

char name[] = "c++";
// scc1 = name;

}

还有一些constness测试

{ // constnes
    const int cc = 42;
    const int cb = cc;
    nothing_but<int> const & sci1 = cc;
    nothing_but<int> const sci2 = sci1 ; // ok
    const nothing_but<int> sci3 = sci1 ; // ok

    wprintf(L"%d", sci1.data()); // ok

    //sci2 = sci1;
    //const int ii1 = sci1;
    //const int ii2 = static_cast<int>(sci1);
    //const int * ii3 = const_cast<int *>(&(sci1));
    const int & ii3 = (const int &)(sci1); // ok
    int & ii4 = (int &)(sci1); // ok
}

{ // compatibility
    // std::vector<nothing_but<bool>> bv{ true, 1 , true };
    std::vector<nothing_but<bool>> bv{ true, true , true };

    nothing_but<bool> bb = bv[1] ;
    bool kb = bb; // OK
    //int k = bb;
    //int j = bv[1];

    std::map< nothing_but<bool>, nothing_but<int> > bm;

    bm[true]  = 1;
    // bm[true]  = 2.3;
    bm[false] = 0;
    // bm[false] = 0.9;
}

等等。被注释的代码不会被编译。

到目前为止,我能看到其潜力,但不确定我是否做对了?您认为这有用吗?如果您认为有用,是否会以不同的方式处理?

请注意,我需要通用解决方案,我称之为“非可转换基本类型”。我希望它可以完成,并且可能很小和通用。



为什么要使用 false == 而不是 !?为什么要使用 mutable - kmdreko
你可以使用clang工具并匹配任何隐式转换。但我担心你会收到很多警告... - Jarod42
1
SFINAE甚至都不需要,因为普通的以const T&为参数的方法本来就更匹配。 - Jarod42
1
也许这是一个适合在 [CodeReview.SE] 上提问的问题? - Lightness Races in Orbit
1
如果你要去那边,你可能想以一种能够得到良好反应的方式提出问题。请先查看如何提问 - Summer
显示剩余5条评论
1个回答

2
你可以使用“方法模板重载”而不是type_traits来完成它:

最初的回答

template<typename T>
struct nothing_but final
{
    // construction
    nothing_but() = default;
    nothing_but(const T& t_)
        : val_(t_)
    {}
    template<typename Other>
    nothing_but(const Other&) = delete;

    // assignment
    nothing_but& operator=(const T& new_val_)
    {
        val_ = new_val_;
        return *this;
    }
    template<typename Other>
    nothing_but& operator=(const Other&) = delete;

    // comparison
    bool operator==(const nothing_but& rhs) const
    {
        return (val_ == rhs.val_);
    }
    bool operator<(const nothing_but& rhs) const
    {
        return (val_ < rhs.val_);
    }

    // conversion
    operator T& ()
    {
        return val_;
    }
    template<typename Other>
    operator T& () = delete;

    // non const value, as other std class types do
    T& data() const
    {
        return (T&)val_; 
    }
    template<typename Other>
    operator Other& () = delete;

private:
    T val_{};
};

相较于模板,非模板更受青睐(请参见C++标准13.3.3或者访问http://www.gotw.ca/gotw/049.htm了解详细信息)。

注:本文不做过多解释,保留HTML标签。


1
@ Lorenz Zhao,这确实是非常简单和非常实用的解决方案。太棒了。简化加一分。更简单的代码总是更好的。 - Chef Gladiator

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接