代码之家  ›  专栏  ›  技术社区  ›  user200783

C++显式构造函数和迭代器

  •  7
  • user200783  · 技术社区  · 15 年前

    请考虑以下代码:

    #include <vector>
    
    struct A
    {
        explicit A(int i_) : i(i_) {}
        int i;
    };
    
    int main()
    {
        std::vector<int> ints;
        std::vector<A> As(ints.begin(), ints.end());
    }
    

    上面应该编译吗?我的感觉是它不应该,因为构造函数被标记了 explicit .

    微软Visual C++同意,给出一个明确的错误信息: cannot convert from 'int' to 'const A'; Constructor for struct 'A' is declared 'explicit'

    然而,使用 Comeau's online compiler ,代码编译成功。

    哪一个是正确的?

    编辑:

    有趣的是,改变 vector set (添加 operator < 使两个编译器都出错。

    但是,改变 vector<int> map<int, int> vector<A> map<A, A> 使两个编译器都接受代码!

    6 回复  |  直到 15 年前
        1
  •  3
  •   Community Dan Abramov    7 年前

    我查看了GCC的STL实现,它应该有类似的行为。这就是原因。

    • A的元素 vector 由接受任意两种类型的通用函数模板初始化 X V 和电话 new( p ) X( v ) 在哪里? v 是一个 V (我有点意译)。这允许显式转换。
    • A的元素 set map 由的私有成员函数初始化 _tree<T,…> 它特别期望 T const & 被传进来。此成员函数不是模板(不是模板的成员),因此如果初始值不能隐式转换为 T ,呼叫失败。(我再次简化了代码。)

    标准不要求显式转换工作或隐式转换在初始化具有范围的容器时不工作。它只是说范围被复制到容器中。对你的目的来说肯定是模棱两可的。

    令人惊讶的是,考虑到他们如何在考虑诸如 the one I had 几周前。

        2
  •  1
  •   Nicolás    15 年前

    我想这取决于 std::vector<A> As(Iterator,Iterator) 在STL的特定实现中实现。

        3
  •  1
  •   David Rodríguez - dribeas    15 年前

    这是一个相当棘手的问题,可能是VisualStudio是对的,而Comeau是错的(这似乎真的很难相信)。

    如果逐字阅读,则标准将根据 复制构造函数 (见引号),字面意思是通过取消对迭代器的引用而获得的对象必须首先转换为T类型,然后才应该调用复制构造函数。此时,使用显式构造函数,代码不应该编译。

    另一方面,期望一个实现直接调用一个以未引用迭代器为参数的构造函数似乎是合理的,在这种情况下,构造函数调用将是显式的,因此代码应该编译。这与下面引文中的确切措辞不符,因为 复制构造函数 为给定的类型t定义为一个构造函数,该构造函数接受对类型t的对象的单个可能常量引用。

    我想不出任何合理的理由不使用comeau方法,我认为(这只是个人的观点)标准中关于向量构造器复杂性的措辞可能应该重新表述为要求 只有n个调用对应的t构造函数 ,在适当的情况下,必须定义为与调用匹配的构造函数。 T( *first ) (也就是说,任何一个构造函数 InputIterator::value_type (通过值或可能的常量引用),或在从 输入器::值类型 到t

    23.2.4.1[lib.vector.cons]/1

    复杂性:构造器模板向量(输入器 第一个,最后一个)只 n调用t的复制构造函数 (其中n是 第一个和最后一个)没有重新分配 如果迭代器的第一个和最后一个是 正向、双向或随机 访问类别。它发出命令n 调用T和的复制构造函数 订单日志n重新分配(如果是) 只是输入迭代器。

    我想知道在给定的情况下vs编译器的行为:

    struct T1;
    struct T2 {
       operator T1 ();
    };
    struct T1 {
       T1( T2 const & ) { std::cout << "T1(T2)" << std::endl; }
    };
    T2::operator T1() {
       std::cout << "T2::operator T1" << std::endl;
       return T1(*this);
    }
    int main() {
       std::vector<T2> v2;
       v2.push_back( T2() );
       std::vector<T1> v1( v2.begin(), v2.end() );
    }
    

    使用G++的结果是 T2::operator T1 不是调用,而是调用 v1 直接从中的元素构造 v2 . 我假设使用vs编译器会使用 T2:算子T1 从中的每个元素转换 V2 到T1元素,然后调用复制构造函数。是这样吗?

        4
  •  1
  •   Beanz    15 年前

    这实际上归结为如何实现STL库的问题,而不是语言规范问题。语言规范中没有任何内容会禁止它工作,也没有任何内容会要求它工作。

    如果编写stl::vector构造函数以尝试使用赋值运算符进行隐式转换,那么它将失败。更可能的是,Microsoft STL实现通过构造函数调用在初始化期间使用返回值优化,在这种情况下,此代码可以正常工作。

    需要注意的是,这项工作的唯一原因是stl::vector构造函数是模板化的,唯一的要求是它是一个输入迭代器,或者更准确地说,它支持输入迭代器的所有必需功能。

    我还想指出,这是一个主要的例子,说明为什么编写跨平台代码通常很困难。有时,您会遇到这样的问题:两个编译器都不一定偏离语言标准,但代码仍然是不可移植的。

        5
  •  0
  •   Igor Zevaka    15 年前

    此代码不在COMEAU中编译:

    class Foo
    {
    public:
     explicit Foo(int bar)
     {
     }
    };
    
    class Bar
    {
     void DoStuff(Foo foo){
    
     }
     void DoStuff2()
     {
      DoStuff(4);
     }
    };
    

    错误信息:

    "ComeauTest.c", line 16: error: no suitable constructor exists to convert from "int"
              to "Foo"
        DoStuff(4);
                ^
    
    1 error detected in the compilation of "ComeauTest.c".
    

    因此,在基本级别上,联机编译器支持显式构造函数。必须与向量/迭代器有关。

    编辑 但是,这可编译为:

    Foo foo = (Foo)5;
    

    这是一个明确的转换,所以没关系。我猜comeau向量类在构造函数的某个地方做了一个显式的强制转换,而微软的库却没有。

    关于显式构造函数的更多信息- http://www.glenmccl.com/tip_023.htm

        6
  •  0
  •   anon    15 年前

    是的,它应该编译。如果不使用构造函数,那么它的明确性就不是问题。