6

有一个不受欢迎的 C 风格转换,我无法阻止它编译。不受欢迎的转换执行从某个类的对象到某个其他类的非常量引用的 C 样式转换。类是不相关的。同时,我喜欢支持从同一类的对象到 const 引用的 C 风格转换。我正在提供一个公共转换运算符来支持理想的演员表。在这种情况下,似乎不可能防止不受欢迎的演员阵容。
转换为非常量引用无法构建(“Sandbox::B::operator Sandbox::A &() ”(在第 30 行声明)不可访问*),不幸的是转换为 const 引用要么失败错误:从 "Sandbox::B" 到 "const Sandbox::A" 的转换函数不止一个:function "Sandbox::B::operator const Sandbox::A &()" function "Sandbox::B::运营商沙盒::A &()" ):

#include <iostream>
#include <string>
#include <cstdlib>

namespace Sandbox {
    class A {
    public:
        A (int i) : _x (i) { }
    private:
        int _x;
    };

    class B {
    public:
        B (const char* m) : _m (m), _a (std::atoi (m)) { }

        /*
         * This one shall be supported.
         */ 
        operator const A& () {
            return _a;
        }
    private:
        /*
         * This one shall be not supported.
         * If this one is disabled both desired and undesired conversions pass the compilation.
         */ 
        operator A& ();

        const std::string _m;
        const A _a;
    };
}

int main () {
    Sandbox::A a (1973);
    Sandbox::B b ("1984");

    /*
     * This is the undesirable cast and it shall fail to compile.
     */
    (Sandbox::A&)b;
    /*
     * This is the desirable cast and it shall pass the compilation.
     */
    (const Sandbox::A&)b;

    return 0;
}

如果我禁用运算符operator A& (),则构建所需和不希望的转换。

我正在使用 gcc、icc 和 MSVC 编译。我无法控制客户端代码并阻止使用 C 样式转换。

4

1 回答 1

3

这应该可以解决问题(在clang3.5上测试):

#include <iostream>
#include <string>
#include <cstdlib>

namespace Sandbox {
  class A {
  public:
    A (int i) : _x (i) { }

    void        fun()
    {
      std::cout << "action" << std::endl;
    }

  private:
    int _x;
  };

  class B {
  public:
    B (const char* m) : _m (m), _a (std::atoi (m)) { }

    /*
     * This one shall be supported.
     */
    template<typename T, typename Enable = typename std::enable_if<std::is_same<T, A>::value, A>::type>
    operator const T& ()
    {
      return _a;
    }

    /*
     * This one shall be not supported.
     * If this one is disabled both desired and undesired conversions pass the compilation.
     */
  private:
    template<typename T, typename Enable = typename std::enable_if<std::is_same<T, A>::value, A>::type>
    operator T& ();

    const std::string _m;
    const A _a;
  };
}

int main () {
  Sandbox::A a (1973);
  Sandbox::B b ("1984");

  /*
   * This is the undesirable cast and it shall fail to compile.
   */
  (Sandbox::A&)b;

  /*
   * This is the desirable cast and it shall pass the compilation.
   */
  (const Sandbox::A&)b;

  return 0;
}

至于为什么你的版本没有做你想做的,这和C-Style cast的规则有关:

当遇到 C 风格的强制转换表达式时,编译器会按以下顺序尝试以下强制转换表达式:

a) const_cast(表达式)

b) 带有扩展的 static_cast(expression):即使基类不可访问(即,此转换忽略私有继承说明符)。同样适用于将指向成员的指针转换为指向明确非虚基成员的指针

c) static_cast(带扩展)后跟 const_cast

d) reinterpret_cast(表达式)

e) reinterpret_cast 后跟 const_cast

选择满足相应强制转换运算符要求的第一个选项,即使它无法编译

免责声明:这个解释主要基于猜测,有多个步骤和复杂的规则,所以我不确定一切是否真的有效,因为我认为我已经理解了它,但你去吧。

由于您转换为引用,reinterpret_cast将始终根据其类型别名规则工作,因此使 C 样式转换失败的唯一方法是使static_cast该类型明确地产生错误。不幸的是,转换规则似乎并不认为用户定义的类型转换const比用户定义的非 cv 限定类型的转换更匹配,即使static_cast目标类型是const合格的,它们也处于同一级别。而使用模板、SFINAE 和参数推导以及从山龙中提取的一些魔法编译器粉末,它可以工作。(是的,这一步对我来说也有点神秘)。

于 2014-10-20T15:27:24.283 回答