【问题标题】:Why does this compile with Visual Studio 2013 but not g++-4.8.1?为什么用 Visual Studio 2013 编译而不是 g++-4.8.1?
【发布时间】:2014-04-25 02:14:50
【问题描述】:

以下示例 (ideone) 在 Windows 7 上使用 Visual Studio 2013 时编译并运行,但在 Ubuntu 13.10 上使用 g++4.8.1 时编译并运行。

#include <cassert>
#include <cstdlib>

#include <array>
#include <iostream>
#include <numeric>
#include <utility>

// Wraps a std::array of TKey/TValue pairs and provides a method
// to randomly select a TKey with TValue bias.
template< typename TKey, typename TValue, std::size_t TSize >
class weights final
{
    public:
        using pair = const std::pair< const TKey, const TValue >;
        using array = const std::array< pair, TSize >;

        weights( array values )
            : values_{ values }
            , sum_{ std::accumulate(values_.begin(), values_.end(), 0, [](TValue total, const pair& p){ return total + p.second; }) }
        {}

        // Implements this algorithm
        // http://stackoverflow.com/a/1761646/331024
        const TKey get() const
        {
            // The real code uses c++11 <random> features,
            // which I've removed for brevity.
            auto weight_rand = static_cast< TValue >( std::rand() % sum_ );

            for ( std::size_t i = 0; i < TSize; ++i )
            {
                if (weight_rand < values_[i].second)
                {
                    return values_[i].first;
                }
                weight_rand -= values_[i].second;
            }
            assert(false);
        }

    private:
        array values_;
        const TValue sum_;
};

enum class direction
{
    NORTH,
    SOUTH,
    EAST,
    WEST
};

// For convenience create a type to map the above
// four-value enumeration to integer weights.
using w4i = weights< direction, int, 4 >;

// Map the directions with a weight.
static const w4i direction_weights = w4i::array{
    {
        w4i::pair{ direction::NORTH, 2 },
        w4i::pair{ direction::EAST, 1 },
        w4i::pair{ direction::SOUTH, 3 },
        w4i::pair{ direction::WEST, 1 }
    }
};

int main()
{
    std::cout << (int)direction_weights.get() << std::endl;    

    return 0;
}

Visual Studio 2013 可以编译和运行代码。 g++-4.8.1编译失败,输出如下错误:

$ g++ -std=c++11 -Wall -Wextra -pedantic weights.cpp -o weights
weights.cpp: In instantiation of ‘weights<TKey, TValue, TSize>::weights(array) [with TKey = direction; TValue = int; long unsigned int TSize = 4ul; weights<TKey, TValue, TSize>::array = const std::array<const std::pair<const direction, const int>, 4ul>]’:
weights.cpp:67:5:   required from here
weights.cpp:20:131: error: could not convert ‘values’ from ‘weights<direction, int, 4ul>::array {aka const std::array<const std::pair<const direction, const int>, 4ul>}’ to ‘const std::pair<const direction, const int>’, sum_{ std::accumulate(values_.begin(), values_.end(), 0, [](TValue total, const pair& p){ return total + p.second; }) }

如何修复/修改它以与两个编译器一起使用?

【问题讨论】:

  • Fwiw,它也会在 clang 3.4 上呕吐(相同/类似的错误)。将values_ 成员类型更改为const array,或者在using array decl 中丢失const,两者之一。
  • @WhozCraig 感谢您的铿锵反馈。我现在所拥有的与您建议的更改之间的功能差异是什么?
  • 这是一些花哨的 const 正确性……还有几个额外的 const 以防万一……
  • @DavidRodríguez-dribeas const 应该是所有内容的默认值! :) 看着那段代码,我可以看到一些错过的const 机会,这让我很紧张!我对@WhozCraig 的问题是合法的:using array = const ... 不意味着数组成员变量也是const
  • g++ 4.7.2 在权重构造函数中给出array must be initialized with a brace-enclosed initializer。 (sum {...线)

标签: c++ visual-studio c++11 g++ visual-studio-2013


【解决方案1】:

您的问题是尝试使用通用初始化,这会引入歧义。获取您的代码并创建最小示例会导致:

不起作用:

struct weights
{
    weights(std::array<int, 1> values)
        : values_{values}
    {}

    std::array<int, 1> values_;
};

作品:

struct weights
{
    weights(std::array<int, 1> values)
        : values_(values)
    {}

    std::array<int, 1> values_;
};

问题在于{values} 应该做什么变得模棱两可。它应该创建一个包含一个元素 (values) 的初始化列表吗?还是应该充当通用初始化程序/大括号初始化程序并具有与括号相同的行为?看起来 GCC 和 clang 正在执行第一个(导致类型不匹配),而 Visual Studio 正在执行第二个(正确类型检查)。

如果您希望它同时适用于两者,请使用括号。

我不知道这是 GCC/clang 或 Visual Studio 中的错误,还是标准中的歧义。

编辑:

举一个更简单的例子,考虑:

std::array<int, 2> a = {1, 2};
std::array<int, 2> b{a}; // error: no viable conversion from 'std::array<int, 2>' to 'value_type' (aka 'int')
std::array<int, 2> b(a); // works

第一个是创建一个初始化列表,其中包含一个 std::array&lt;int, 2&gt; 对象,而第二个是正确调用复制构造函数。

【讨论】:

  • 看起来像图书馆问题。相同的语法适用于 std::vector。
  • @n.m.:我已经提交了 llvm/libc++ buggcc/libstdc++ bug
  • @n.m. vector 不是聚合,因此适用不同的规则。我很确定这是 DR1467 而 VC++ 是错误的
  • @JonathanWakely:那么我应该关闭我的错误报告吗?
【解决方案2】:

这不是std::array 中的错误

struct array {
  int i;
};

int main()
{
  array a;
  array b{a};
}

G++ 和 Clang 都拒绝这一点,因为它们是标准要求的,请参阅在我报告 GCC PR 51747 后打开的 DR 1467

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-12-18
    • 1970-01-01
    • 2019-11-15
    • 1970-01-01
    • 2015-10-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多