在模板类型推导之前评估noexcept指定符

时间:2018-07-24 14:26:52

标签: c++ c++11 language-lawyer noexcept

请参见以下代码:

#include <utility>

struct A {
  A(int, int) {}
};

struct tag {};

template <class... Args>
struct is_noexcept {
  static constexpr bool value = noexcept(A{std::declval<Args>()...});
};

struct B : A {
  //#1
  template <class... Args>
  B(tag, Args&&... args) noexcept(/*Here*/is_noexcept<Args...>::value) :
    A{std::forward<Args>(args)...} {}

  //#2
  B(int x, int y) : A{x, y} {}
};

int main()
{
  B x{0, 0};
}

此代码似乎已被GCC / Clang接受,但MSVC 2017拒绝了它。 似乎MSVC编译器在了解#1不是适当的重载之前(由于tagint之间不兼容),尝试计算noexcept指定符,因此应将其丢弃。因此,它尝试评估is_noexcept<int>::value并发现noexcept(A{std::declval<int>()})格式错误。由于这不是在立即发生的情况下发生的,所以SFINAE并不是在这里出现的,所以很难出错。

(实际上,我对此不太确定,但是我已经确认,如果我在noexcept(A{std::declval<Args>()...})处放置is_noexcept<Args...>::value而不是/*Here*/来使故障立即在上下文中,MSVC编译器愉快地删除了#1并调用了#2。对吗?)

我怀疑GCC / Clang是正确的,而MSVC是错误的,但是哪一个是正确的?

1 个答案:

答案 0 :(得分:5)

这是MSVC错误。由于CWG 1330[except.spec]/13中的规则是:

  

在以下情况下,认为需要 一个异常规范

     
      
  • 在表达式中,该函数是唯一的查询结果或一组重载函数([basic.lookup],[over.match],[over.over])的选定成员;
  •   
  • 该函数是odr使用的,或者,如果它出现在未求值的操作数中,则如果该表达式可能被求值,则将被使用;
  •   
  • 将异常规范与另一个声明(例如,显式专门化或覆盖的虚函数)的规范进行比较;
  •   
  • 函数已定义;或
  •   
  • 调用默认函数的默认特殊成员函数需要异常规范。 [注:默认声明不需要对派生函数的隐式异常规范进行评估,而需要评估基本成员函数的异常规范,但是显式的noexcept-specifier需要隐式异常规范比较。 -注释]
  •   
     

仅在需要时才如上所述评估默认特殊成员函数的异常规范;类似地,仅在需要时才实例化功能模板或类模板的成员函数的专业化的 noexcept-specifier

不需要#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <errno.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdint.h> #include <sys/time.h> #include <math.h> #include <string> #include <cstdlib> #define N ((cols+1)*rows + cols) int main(int argc, char *argv[]) { int fdr, fdw; FILE *ftime_rw; FILE *fp; FILE *U, *V, *RI; int rc, donebytes; int *tologic, *fromlogic; pid_t pid; int i,j; struct timeval tv1, tv2; double time_rw; char *buf; int temp; int rows= 575; int cols= 18689; fdr = open("/dev/xillybus_read_32", O_RDONLY); fdw = open("/dev/xillybus_write_32", O_WRONLY); if ((fdr < 0) || (fdw < 0)) { perror("Failed to open Xillybus device file(s)"); exit(1); } U = fopen("U.txt", "r"); V = fopen("V.txt", "r"); RI = fopen("RI.txt", "a"); if(U==NULL || V==NULL || RI==NULL ) { printf("Write in their respective files!\n"); exit(1); } for (i=0; i<rows; i++) // Read { fscanf(U,"%d", &temp); fprintf(RI,"%d\n", temp); } for(j=0; j<rows; j++) { for (i=0; i<cols; i++) // Read { fscanf(V,"%d", &temp); fprintf(RI,"%d ", temp); } fclose(U); fclose(V); fclose(RI); tologic = (int*)malloc(sizeof(int)* N); if (!tologic) { fprintf(stderr, "failed to allocate memory\n"); exit(1); } RI = fopen("RI.txt", "r"); for(j=0; j<rows; j++) { for (i=0; i<cols; i++) fscanf(RI,"%d", &tologic[N]); fclose(RI); pid = fork(); // writer + reader if (pid < 0) { perror("Failed to fork()"); exit(1); } if (pid) // writer process { close(fdr); buf = (char *)tologic; donebytes=0; gettimeofday(&tv1, NULL); // start count time while (donebytes < sizeof(int)*N) // write N integers { rc = write(fdw, tologic + donebytes, sizeof(int)*N - donebytes); if ((rc < 0) && (errno == EINTR)) continue; if (rc <= 0) { perror("write() failed"); exit(1); } donebytes += rc; } gettimeofday(&tv2, NULL); // stop count time time_rw = (double) (tv2.tv_usec-tv1.tv_usec); printf("Writer. Total time = %f usec\n", time_rw); if(close(fdw)==-1) printf("ERROR closing write_32 file!\n"); return 0; } else // reader process { close(fdw); fromlogic =(int*)malloc(sizeof(int)* N); if (! fromlogic) { fprintf(stderr, "failed to allocate memory\n"); exit(1); } buf = (char *)fromlogic; donebytes = 0; donebytes = 0; gettimeofday(&tv1, NULL); // start count time while (donebytes < sizeof(int)*rows) // read num_rows integers { rc = read(fdr, fromlogic + donebytes, sizeof(int)*rows - donebytes); if ((rc < 0) && (errno == EINTR)) continue; if (rc < 0) { perror("read() failed"); exit(1); } if (rc == 0) { fprintf(stderr, "Reached read EOF!? Should never happen.\n"); exit(0); } donebytes += rc; } gettimeofday(&tv2, NULL); // stop count time time_rw = (double) (tv2.tv_usec-tv1.tv_usec); printf("Reader. Total time = %f usec\n", time_rw); for (i=0; i<rows; i++) // Integers read from logic printf("Reader process : %d\n", fromlogic[i]); sleep(1); // Let debug output drain (if used) if(close(fdr)==-1) printf("ERROR closing read_32 file!\n"); return 0; } } } } 的异常说明(我们不满足任何这些要求),因此不应实例化它。

还请注意,异常规范中的替换失败不是立即上下文的一部分,并且不是SFINAE友好的错误。