【问题标题】:Conditionally enable non-template function c++有条件地启用非模板函数 c++
【发布时间】:2019-05-21 21:08:32
【问题描述】:

背景

我正在使用 MPI 进行高性能计算的 C++ 项目。我有一个带有几个不同重载的函数,用于将不同的类型转换为字符串:

void append(std::string& s, int value);
void append(std::string& s, void* value);
void append(std::string& s, MPI_Request request); 

这在我使用 Open MPI 时运行良好。在 OpenMPI 中,MPI_Requestompi_request_t* 的别名,因此每个重载都有不同的签名。

不过,最近我尝试使用 MPICH 编译代码。在MPICH中,MPI_Requestint的别名,结果是上面的代码编译失败,因为appendint定义了两次:

/home/me/NimbleSM/src/mpi-buckets/src/mpi_err.hpp: At global scope:
/home/me/NimbleSM/src/mpi-buckets/src/mpi_err.hpp:28:6: error: redefinition of ‘void append(std::__cxx11::string&, int)’
 void append(std::string& s, int i) { s.append(std::to_string(i)); }
      ^~~
/home/me/NimbleSM/src/mpi-buckets/src/mpi_err.hpp:17:6: note: ‘void append(std::__cxx11::string&, MPI_Request)’ previously defined here
 void append(std::string& s, MPI_Request request)

问题

我应该如何编写append(std::string&, MPI_Request),以便编译器在MPI_Request 被定义为int 时忽略它,但在MPI_Request 是库类型时识别它?

尝试的解决方案:enable_if 失败

我尝试编写基于std::enable_if 的解决方案,其中仅当MPI_Requestint 的类型不同时才启用该功能。

auto append(std::string& s, MPI_Request request)
    -> typename std::enable_if<!std::is_same<MPI_Request, int>::value, void>::type
{ 
    str(s, (void*)request); 
}

这会失败,因为当MPI_Requestint 相同时,该语句始终为假,并且由于它不依赖于任何模板参数,因此编译器完全拒绝编译它。

我该如何解决这个问题,并使append 取决于MPI_Requestint 不同?

【问题讨论】:

  • 如果MPI_Requestint,您是否想要与int 重载相同的行为?
  • 没有办法用非模板模拟 SFINAE。 SFINAE 的主要使用点是避免使用不能在函数体中使用的类型来实例化函数。非模板不允许你这样做。
  • 如果 MPI_Request 是 int,我希望编译器忽略该函数。所以它只是假装 MPI_Request 重载不存在
  • 也许有一个设计问题需要在更高的层次上解决,因为无论你做什么,你都永远无法区分 void append(std::string&amp; s, int value)void append(std::string&amp; s,int mpi_request)(相同的签名)
  • 我不需要区分它们;如果 MPI_Request 不是 int,我只需要有一个重载。如果它是一个 int,我可以调用 int 版本(我只需要非 int 版本不存在)

标签: c++ c++11 mpi portability


【解决方案1】:

这很不幸。底线是enable_if 只能在需要 T - 模板的 SFINAE 上下文中使用。为了重复您的想法,我们可以在返回类型中指定我们的要求,以便模板仅匹配 MPI_Request 并且仅当 MPI_Request 类型不是 int 时。

#include <string>
#include <type_traits>
using MPI_Request = int;// Or something else

template<typename T>
using T_is_MPI_and_not_also_int = std::conjunction<std::is_same<T,MPI_Request>, std::negation<std::is_same<MPI_Request,int>>>;

template<typename T>
std::enable_if_t<T_is_MPI_and_not_also_int<T>::value,void> 
append(std::string& s, T request);

Full example,您甚至可以看到哪一行 cout 被内联到 main 中。

【讨论】:

    【解决方案2】:

    你可以把它做成一个带有默认模板参数的函数模板,这样SFINAE就可以工作了:

    void append(std::string& s, int value);
    void append(std::string& s, void* value);
    
    template <typename MPI_Request_ = MPI_Request,
        typename std::enable_if<
            !std::is_same<MPI_Request_, int>::value
            && std::is_same<MPI_Request_, MPI_Request>::value
        , int>::type = 0>
    void append(std::string& s, MPI_Request_ request)
    { 
        str(s, (void*)request);
        // Or, if you want the implementation in the source file,
        // call another function like: append_mpi_request(s, request);
    }
    

    Demo

    【讨论】:

    • 你的答案比我的干净。
    【解决方案3】:

    这是一个低技术含量的解决方案。使用预处理器宏。

    void append(std::string& s, int value);
    void append(std::string& s, void* value);
    
    #ifdef MPI_REQUEST_IS_INT
    // No need to declare append() for MPI_Request
    #else
    void append(std::string& s, MPI_Request request); 
    #endif
    

    在 .cpp 文件中使用相同的策略。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-07-18
      • 2021-11-25
      • 2021-07-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多