在clang中显式指定的参数无效,但在gcc中成功编译 - 谁错了? [英] Invalid explicitly-specified argument in clang but successful compilation in gcc — who's wrong?

查看:620
本文介绍了在clang中显式指定的参数无效,但在gcc中成功编译 - 谁错了?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以下代码在g ++中无问题地编译:

  #include< iostream& 
#include< string>
#include< tuple>

template< typename T>
void test(const T& value)
{
std :: tuple< int,double> X;
std :: cout<< std :: get< value>(x);
}

int main(){
test(std :: integral_constant< std :: size_t,1>());
}

我使用这个命令:

  g ++ test.cpp -o test -std = c ++ 14 -pedantic -Wall -Wextra 

但是当我把 g ++ 切换到 clang ++ 0和clang ++ 3.6.0),我得到以下错误:

  test.cpp:9:18:函数调用'get'
std :: cout<< std :: get< value>(x);
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1。 >'requested here
test(std :: integral_constant< std :: size_t,1>());
^ ~~~~~~~~~~~~~~~
< skipped>

/usr/bin/../lib/gcc/x86_64-linux-gnu/5.1.0/../../../../include/c++/5.1.0/元组:867:5:注意:候选模板忽略:模板参数_Tp的显式指定的参数无效
get(tuple< _Types ...>& __t)noexcept
^

和类似注意:其他重载< c $ c> std :: get 。



但我传递 std :: integral_constant test(),这是一个常量表达式,为什么会是模板参数的无效的显式指定的参数?



我注意到如果我改变 test() const T& const T ,然后clang编译成功。我以某种方式通过引用传递 constexpr 质量 integral_constant

解决方案

由于没有一周的答案,我会发布我的愿景。我远不是一个专家在语言分层,实际上我会认为自己一个完整的新手,但仍然。以下是基于我对标准的阅读,以及我最近的问题



因此,首先让我们以下面的方式重写代码:

  struct A {
constexpr operator int()const {return 42; }
};

template< int>
void foo(){}

void test(const A& value){
foo< value>();
}

int main(){
A a {};
test(a);
}

它具有相同的行为(使用gcc构建并失败, ),但是




  • 不能在 test()为了确保问题与类型扣除无关,

  • 使用mocks而不是 std 成员来确保这是不是它们的实现问题,

  • 并且有一个显式变量 a ,而不是临时的,稍后解释。 >


这里发生了什么?我将引用 N4296 。 p>

我们有一个带有非类型参数的模板 foo



[14.3.2(temp.arg.nontype)] / 1:


非类型模板的模板参数参数必须是
模板参数类型的
转换常量表达式(5.20)。


因此,模板参数,即 value 应该是 int 类型的转换后的常量表达式。



[5.20(expr.const)] / 4:


类型T的转换常数表达式为一个表达式,隐式地
转换为类型T,其中转换的表达式是常量
表达式,隐式转换序列只包含




  • 用户定义的转化次数,

  • ...(不相关的项目符号已删除)




我们的表达式( value )可以隐式转换为 int ,转换序列只包含用户定义的转换。因此,两个问题仍然存在:转换的表达式是否为常量表达式以及引用绑定(如果有)是否直接绑定。



短语转换的表达式,我认为,意味着已经转换为 int 的表达式,就像 static_cast< int>(value) ,而不是原始表达式( value )。为此,



[5.20(expr.const)] / 2:


条件表达式 e 是核心常量表达式,除非
评估 e 的抽象机器(1.9),
将评估以下表达式之一:




  • ... )


评估我们的表达式, static_cast< int& ,只引用 A :: operator int()的评估,它是 constexpr ,因此被明确允许。没有任何成员 A (如果有的话)被评估,任何其他都不评估。



code> static_cast< int>(value)是一个常数表达式。



不清楚我对这是指哪个过程。但是,无论如何我们在代码中只有一个引用( const A& value ),它直接绑定到变量 a main (这是我为什么引入 a 的原因)。



事实上,直接绑定在[8.5.3(dcl.init.ref)] / 5结尾定义:


在所有情况下,除了最后一个(即,从初始化器表达式创建和初始化
临时),引用被称为
直接绑定到初始化器表达式。


这个最后的情况似乎指的是5.2,非直接绑定意味着从一个临时的初始化(如 const int& i = 42; ),而不是我们有一个非临时 a 的情况。



UPD :我问了另一个问题以检查我对上述标准的理解是否正确。






所以底线是代码应该是有效,ang是错误的。我建议你提交一个bug铛bug跟踪,参考这个问题。



UPD :提交一个错误报告


The following code compiles without problems in g++:

#include <iostream>
#include <string>
#include <tuple>

template<typename T>
void test(const T& value)
{
    std::tuple<int, double> x;
    std::cout << std::get<value>(x);
}

int main() {
    test(std::integral_constant<std::size_t,1>());
}

I used this command:

g++ test.cpp -o test -std=c++14 -pedantic -Wall -Wextra

But when I switch g++ to clang++ (with g++ 5.1.0 and clang++ 3.6.0), I get the following errors:

test.cpp:9:18: error: no matching function for call to 'get'
    std::cout << std::get<value>(x);
                 ^~~~~~~~~~~~~~~
test.cpp:13:5: note: in instantiation of function template specialization 'test<std::integral_constant<unsigned long, 1> >' requested here
    test(std::integral_constant<std::size_t,1>());
         ^~~~~~~~~~~~~~~
<skipped>

/usr/bin/../lib/gcc/x86_64-linux-gnu/5.1.0/../../../../include/c++/5.1.0/tuple:867:5: note: candidate template ignored: invalid explicitly-specified argument for template parameter '_Tp'
    get(tuple<_Types...>& __t) noexcept
    ^

And similar note: entries for other overloads of std::get.

But I'm passing std::integral_constant to test(), which is a constant-expression, why would it be an "invalid explicitly-specified argument" for the template parameter? Is it a clang bug or am I doing something wrong here?

I've noticed that if I change parameter for test() from const T& to const T, then clang compiles successfully. Do I somehow lose constexpr quality of integral_constant by passing it by reference?

解决方案

As there is no answer for a week, I will post my vision. I am far from being an expert at language-layering, actually I would consider myself a complete novice, but still. The following is based on my reading of standard, as well on my recent question.

So, first of all let's rewrite the code the following way:

struct A {
    constexpr operator int() const { return 42; }
};

template <int>
void foo() {}

void test(const A& value) {
    foo<value>();
}

int main() {
    A a{};
    test(a);
}

It exhibits the same behavior (builds with gcc and fails with similar error with clang), but:

  • is free from template type deduction at test(), to make sure the problem has nothing to do with type deduction,
  • uses 'mocks' instead of std members to make sure this is not a problem with their implementation,
  • and has an explicit variable a, not a temporary, to be explained later.

What does happen here? I will quote N4296.

We have a template foo with a non-type parameter.

[14.3.2(temp.arg.nontype)]/1:

A template-argument for a non-type template-parameter shall be a converted constant expression (5.20) of the type of the template-parameter.

So template argument, i.e. value, should be a converted constant expression of type int.

[5.20(expr.const)]/4:

A converted constant expression of type T is an expression, implicitly converted to type T, where the converted expression is a constant expression and the implicit conversion sequence contains only

  • user-defined conversions,
  • ... (irrelevant bullets dropped)

and where the reference binding (if any) binds directly.

Our expression (value) can be implicitly converted to type int, and the conversion sequence contains only user-defined conversions. So the two questions remain: whether "the converted expression is a constant expression" and whether "the reference binding (if any) binds directly".

For the first question, the phrase "the converted expression", I think, means the expression as already converted to int, that is something like static_cast<int>(value), not the original expression (value). For that,

[5.20(expr.const)]/2:

A conditional-expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine (1.9), would evaluate one of the following expressions:

  • ... (a long list omitted)

Evaluation of our expression, static_cast<int>(value), leads only to evaluation of A::operator int(), which is constexpr, and thus is explicitly allowed. No members of A (if there were any) are evaluated, neither anything else is evaluated.

Therefore, static_cast<int>(value) is a constant expression.

For the second question, about reference binding, it is not clear for me to which process this refers at all. However, anyway we have only one reference in our code (const A& value), and it binds directly to the variable a of main (and this is the reason why I introduced a).

Indeed, direct binding is defined at the end of [8.5.3(dcl.init.ref)]/5:

In all cases except the last (i.e., creating and initializing a temporary from the initializer expression), the reference is said to bind directly to the initializer expression.

This "last" case seem to refer to 5.2, and non-direct binding means initialization from a temporary (like const int& i = 42;), not our case when we have a non-temporary a.

UPD: I asked a separate question to check whether my understanding of the standard, presented above, is correct.


So the bottomline is that the code should be valid, and clang is wrong. I suggest you filing a bug to clang bug tracker, with a reference to this question. Or if for whatever reason you will not file a bug, let me know, I will file it.

UPD: filed a bug report

这篇关于在clang中显式指定的参数无效,但在gcc中成功编译 - 谁错了?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆