为什么我无法阻止不受欢迎的C风格演员表编译?

时间:2014-10-20 10:10:07

标签: c++ casting standards conversion-operator

我不能阻止编译,这是一种不受欢迎的C风格演员。不受欢迎的强制转换执行从某个类的对象到其他类的非const引用的C样式强制转换。这些课程是无关的。同时我喜欢支持从同一个类的对象到const引用的C样式转换。我提供了一个公共转换运算符来支持理想的转换。在这种情况下,似乎不可能防止不良铸件 转换为非const引用无法构建(" Sandbox :: B :: operator Sandbox :: A&()"(在第30行声明)无法访问* ),不幸的是转换为const引用要么失败错误:来自&#34的多个转换函数; Sandbox :: B"到" const Sandbox :: A&# 34;适用:             function" Sandbox :: B :: operator const Sandbox :: A&()"             function" Sandbox :: B :: operator Sandbox :: 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风格的转换。

1 个答案:

答案 0 :(得分: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风格的演员规则有关:

  

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

     

a)const_cast(表达式)

     

b)static_cast(表达式),   带扩展名:指针或对派生类的引用   另外允许转换为指针或引用明确的   基类(反之亦然),即使基类不可访问   (也就是说,此强制转换会忽略私有继承说明符)。相同   适用于将指向成员的指针指向指向成员的指针   明确的非虚拟基础

     

c)接下来是static_cast(带扩展名)   通过const_cast

     

d)reinterpret_cast(表达式)

     

E)   reinterpret_cast后跟const_cast

     

第一个选择   满足各个铸造操作员的要求   选中,即使无法编译

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

由于您投射到引用,reinterpret_cast将始终基于其rules of type aliasing工作,因此使C-Style强制转换失败的唯一方法是对其进行static_cast类型明确地产生错误。遗憾的是,转换规则似乎并未将用户定义的转换为const类型视为比用户定义的转换为非cv限定类型更好的匹配,即使它们都处于同一级别static_cast目标类型为const限定。使用模板,SFINAE和参数推导开始并且使用从山龙中提取的一些魔术编译器粉末,它可以工作。 (是的,这一步对我来说也有点神秘)。