【问题标题】:c++ return from templatec++ 从模板返回
【发布时间】:2016-06-12 17:46:44
【问题描述】:

我对使用 C++ 中的模板进行泛型编程有点陌生,并且对如何从模板化函数返回对象有疑问。这是 mlpack 库的神经网络模块的一部分。这是来自 here 的 feedforward_network_test.cpp。如果我理解正确,模板化函数 BuildVanillaNetwork 的设置方式,它可以传递不同类型的网络参数来构建神经网络。我想要这个函数返回它构建的 FFN 对象,这样我就可以从我调用它的地方访问它。我对那边的代码做了一些小改动:

template <typename PerformanceFunction,
         typename OutputLayerType,
         typename PerformanceFunctionType,
         typename MatType = arma::mat
         >
mlpack::ann::FFN<> BuildVanillaNetwork(MatType& trainData,
        MatType& trainLabels,
        MatType& testData,
        MatType& testLabels,
        const size_t hiddenLayerSize,
        const size_t maxEpochs,
        const double classificationErrorThreshold)
{
    // input layer
    mlpack::ann::LinearLayer<> inputLayer(trainData.n_rows, hiddenLayerSize);
    mlpack::ann::BiasLayer<> inputBiasLayer(hiddenLayerSize);
    mlpack::ann::BaseLayer<PerformanceFunction> inputBaseLayer;

    // hidden layer
    mlpack::ann::LinearLayer<> hiddenLayer1(hiddenLayerSize, trainLabels.n_rows);
    mlpack::ann::BiasLayer<> hiddenBiasLayer1(trainLabels.n_rows);
    mlpack::ann::BaseLayer<PerformanceFunction> outputLayer;

    // output layer
    OutputLayerType classOutputLayer;

    auto modules = std::tie(inputLayer, inputBiasLayer, inputBaseLayer, hiddenLayer1, hiddenBiasLayer1, outputLayer);
    mlpack::ann::FFN<decltype(modules), decltype(classOutputLayer), mlpack::ann::RandomInitialization, PerformanceFunctionType> net(modules, classOutputLayer);
    net.Train(trainData, trainLabels);
    MatType prediction;
    net.Predict(testData, prediction);

    double classificationError;
    for (size_t i = 0; i < testData.n_cols; i++)
    {
        if (arma::sum(arma::sum(arma::abs(prediction.col(i) - testLabels.col(i)))) != 0)
        {
            classificationError++;
        }
    }

     classificationError = double(classificationError) / testData.n_cols;

    std::cout << "Classification Error = " << classificationError * 100 << "%" << std::endl;

    return net;
}

这里是主要功能:

int main(int argc, char** argv)
{
    arma::mat dataset;
    mlpack::data::Load("../data/thyroid_train.csv", dataset, true);
    arma::mat trainData = dataset.submat(0, 0, dataset.n_rows - 4, dataset.n_cols - 1);
    arma::mat trainLabels = dataset.submat(dataset.n_rows - 3, 0, dataset.n_rows - 1, dataset.n_cols - 1);

    mlpack::data::Load("../data/thyroid_test.csv", dataset, true);
    arma::mat testData = dataset.submat(0, 0, dataset.n_rows - 4, dataset.n_cols - 1);
    arma::mat testLabels = dataset.submat(dataset.n_rows - 3, 0, dataset.n_rows - 1, dataset.n_cols - 1);

    const size_t hiddenLayerSize = 8;
    const size_t maxEpochs = 200;
    const double classificationErrorThreshold = 0.1;

    auto myFFN = BuildVanillaNetwork<mlpack::ann::LogisticFunction, mlpack::ann::BinaryClassificationLayer, mlpack::ann::MeanSquaredErrorFunction>
        (trainData, trainLabels, testData, testLabels, hiddenLayerSize, maxEpochs, classificationErrorThreshold);

    return 0;
}

当我编译这个时,我得到以下错误:

[100%] Building CXX object CMakeFiles/ff_nn.dir/src/ff_nn.cpp.o /home/username/project-yanack/mlpack_nn/src/ff_nn.cpp:24:18: error: wrong number of template arguments (0, should be 4)  mlpack::ann::FFN<> BuildVanillaNetwork(MatType& trainData,
                  ^ In file included from /home/username/project-yanack/mlpack_nn/src/ff_nn.cpp:16:0: /usr/local/include/mlpack/methods/ann/ffn.hpp:35:7: error: provided for ‘template<class LayerTypes, class OutputLayerType, class InitializationRuleType, class PerformanceFunction> class mlpack::ann::FFN’  class FFN
       ^ /home/username/project-yanack/mlpack_nn/src/ff_nn.cpp: In instantiation of ‘int BuildVanillaNetwork(MatType&, MatType&, MatType&, MatType&, size_t, size_t, double) [with PerformanceFunction
= mlpack::ann::LogisticFunction; OutputLayerType = mlpack::ann::BinaryClassificationLayer; PerformanceFunctionType = mlpack::ann::MeanSquaredErrorFunction; MatType = arma::Mat<double>; size_t = long unsigned int]’: /home/username/project-yanack/mlpack_nn/src/ff_nn.cpp:83:112:   required from here /home/username/project-yanack/mlpack_nn/src/ff_nn.cpp:64:12: error: cannot convert ‘mlpack::ann::FFN<std::tuple<mlpack::ann::LinearLayer<arma::Mat<double>, arma::Mat<double> >&, mlpack::ann::BiasLayer<arma::Mat<double>, arma::Mat<double> >&, mlpack::ann::BaseLayer<mlpack::ann::LogisticFunction, arma::Mat<double>, arma::Mat<double> >&, mlpack::ann::LinearLayer<arma::Mat<double>, arma::Mat<double> >&, mlpack::ann::BiasLayer<arma::Mat<double>, arma::Mat<double> >&, mlpack::ann::BaseLayer<mlpack::ann::LogisticFunction, arma::Mat<double>, arma::Mat<double> >&>, mlpack::ann::BinaryClassificationLayer, mlpack::ann::RandomInitialization, mlpack::ann::MeanSquaredErrorFunction>’ to ‘int’ in return
     return net;
            ^ make[2]: *** [CMakeFiles/ff_nn.dir/src/ff_nn.cpp.o] Error 1 make[1]: *** [CMakeFiles/ff_nn.dir/all] Error 2 make: *** [all] Error 2

对于解决此问题的任何帮助表示赞赏。另外,如果我能获得解释此代码中使用的各种概念的教程的链接,那就太好了。

EDIT-1

我把函数头改成这样:

template <typename PerformanceFunction,
         typename OutputLayerType,
         typename PerformanceFunctionType,
         typename MatType = arma::mat
         >
mlpack::ann::FFN<PerformanceFunction, OutputLayerType, PerformanceFunctionType, MatType> BuildVanillaNetwork(MatType& trainData,
        MatType& trainLabels,
        MatType& testData,
        MatType& testLabels,
        const size_t hiddenLayerSize,
        const size_t maxEpochs,
        const double classificationErrorThreshold)

但是编译的时候还是报错:

[100%] Building CXX object CMakeFiles/ff_nn.dir/src/ff_nn.cpp.o
In file included from /home/username/project-yanack/mlpack_nn/src/ff_nn.cpp:16:0:
/usr/local/include/mlpack/methods/ann/ffn.hpp: In instantiation of ‘class mlpack::ann::FFN<mlpack::ann::LogisticFunction, mlpack::ann::BinaryClassificationLayer, mlpack::ann::MeanSquaredErrorFunction, arma::Mat<double> >’:
/home/username/project-yanack/mlpack_nn/src/ff_nn.cpp:83:112:   required from here
/usr/local/include/mlpack/methods/ann/ffn.hpp:361:55: error: incomplete type ‘std::tuple_size<mlpack::ann::LogisticFunction>’ used in nested name specifier
       size_t Max = std::tuple_size<LayerTypes>::value - 1,
                                                       ^
/usr/local/include/mlpack/methods/ann/ffn.hpp:369:55: error: incomplete type ‘std::tuple_size<mlpack::ann::LogisticFunction>’ used in nested name specifier
       size_t Max = std::tuple_size<LayerTypes>::value - 1,
                                                       ^
/home/username/project-yanack/mlpack_nn/src/ff_nn.cpp: In instantiation of ‘mlpack::ann::FFN<PerformanceFunction, OutputLayerType, PerformanceFunctionType, MatType> BuildVanillaNetwork(MatType&, MatType&, MatType&, MatType&, size_t, size_t, double) [with PerformanceFunction = mlpack::ann::LogisticFunction; OutputLayerType = mlpack::ann::BinaryClassificationLayer; PerformanceFunctionType = mlpack::ann::MeanSquaredErrorFunction; MatType = arma::Mat<double>; size_t = long unsigned int]’:
/home/username/project-yanack/mlpack_nn/src/ff_nn.cpp:83:112:   required from here
/home/username/project-yanack/mlpack_nn/src/ff_nn.cpp:64:12: error: could not convert ‘net’ from ‘mlpack::ann::FFN<std::tuple<mlpack::ann::LinearLayer<arma::Mat<double>, arma::Mat<double> >&, mlpack::ann::BiasLayer<arma::Mat<double>, arma::Mat<double> >&, mlpack::ann::BaseLayer<mlpack::ann::LogisticFunction, arma::Mat<double>, arma::Mat<double> >&, mlpack::ann::LinearLayer<arma::Mat<double>, arma::Mat<double> >&, mlpack::ann::BiasLayer<arma::Mat<double>, arma::Mat<double> >&, mlpack::ann::BaseLayer<mlpack::ann::LogisticFunction, arma::Mat<double>, arma::Mat<double> >&>, mlpack::ann::BinaryClassificationLayer, mlpack::ann::RandomInitialization, mlpack::ann::MeanSquaredErrorFunction>’ to ‘mlpack::ann::FFN<mlpack::ann::LogisticFunction, mlpack::ann::BinaryClassificationLayer, mlpack::ann::MeanSquaredErrorFunction, arma::Mat<double> >’
     return net;
            ^
make[2]: *** [CMakeFiles/ff_nn.dir/src/ff_nn.cpp.o] Error 1
make[1]: *** [CMakeFiles/ff_nn.dir/all] Error 2
make: *** [all] Error 2

此外,FFN 类的签名 (here) 似乎与我在此函数中的签名不同。这可能是个问题吗?如果是,我该如何解决,因为据我所知,这些类型名并不是真正的“类型”。

谢谢。

【问题讨论】:

  • 哪个 C++ 标准? 11? 14 ?
  • @Joel:C++11 asaik。因为我将 CMAKE_CXX_FLAGS 设置为 -std=c++11。
  • 在我的手机上,所以我无法真正输入解决方案,但您将不得不以某种方式指定正确的返回类型。为了使其更可口,您可以使用别名声明将其分成几部分。此外,如果您愿意切换到 C++14,则可以避开所有这些废话,并将返回类型替换为 auto

标签: c++ templates mlpack


【解决方案1】:

问题是您将BuildVanillaNetwork 函数定义为:

mlpack::ann::FFN<> BuildVanillaNetwork(...)

当涉及到模板时,错误消息通常很难被人类阅读,但通读这些行会得到如下信息:

错误:模板参数的数量错误(0,应该是 4)...为“模板类 mlpack::ann::FFN”提供的

其余的错误都是由这个引起的(基本上,因为它不理解那个函数的返回类型,编译器假设它是int,然后它抱怨它不能转换netint)。

因此,您必须实际指定返回类型的模板参数。您在函数体中使用decltype 来推断它们(这发生在编译时,而不是运行时),但在原型中它不会那么容易。有一种方法可以使用decltype 来声明函数的返回类型,但在这种情况下它对您没有多大帮助。所以你不妨继续明确地写出来。

【讨论】:

  • @lonut:我明白了。如果我明确地编写它们,那么我将不得不为具有不同参数的 FFN 编写多个函数,对吗?此外,第一个模板参数是 decltype(modules) ,它是一堆东西的 std::tie (args 的数量会有所不同)。所以我不确定如何自己推断出类型来明确写出来。
  • FFN 的模板参数可能取决于函数的模板参数,对吧?在这种情况下,您不必编写多个函数,也不能编写多个仅返回类型不同的函数。
  • std::tie 的参数可以变化,但它是一个模板类,因此模板参数将在编译时完全解析。
  • @lonut:从FFN代码来看,这似乎是FFN的定义:FFN
  • @lonut:如果我编译它,我会收到一条错误消息,指出 LayerTypes 等未在范围内声明。所以,我仍然不确定如何根据它返回的内容来定义我的函数,因为返回的对象类型是在函数内部推导出来的。
【解决方案2】:

mlpack::ann::FFN&lt;&gt; BuildVanillaNetwork(MatType&amp; trainData,

将其更改为包含模板中的参数的内容,例如:

mlpack::ann::FFN<
    PerformanceFunction,
    OutputLayerType,
    PerformanceFunctionType,
    MatType
> BuildVanillaNetwork(MatType& trainData,...

如果您的编译器支持,请尝试decltype(auto)

auto BuildVanillaNetwork(MatType& trainData,`...) -> decltype(auto) {...

【讨论】:

  • 我不确定这是否是问题所在。因为即使我只是调用 BuildVanillaNetwork 而不将其返回存储在任何地方,我在编译期间也会遇到相同的错误。另外,我不确定如何确定实际类型,因为 FFN 的第一个模板参数是 decltype(modules),我理解它决定了运行时的类型。
  • 好的,抱歉,第一个错误抱怨缺少模板参数。对答案进行了更改。
  • 所以这个错误是因为decltype(net) != mlpack::ann::FFN&lt;&gt;。是否存储结果并不重要,您仍然在告诉编译器该函数返回后者,而实际上它返回的是前者。模板参数需要匹配才能使类型相同。
  • @flatmouse: 如果我尝试 我收到一个错误,指出在没有参数列表的情况下无效使用模板名称
  • 而且类型仍然在编译时确定
【解决方案3】:

您可以使用以下模式在某种程度上简化返回类型推断:

template<typename PerformanceFunction, typename OutputLayerType,
    typename PerformanceFunctionType, typename MatType>
struct BuildVanillaNetworkHelper
{
    using LinearLayer = mlpack::ann::LinearLayer<>;
    using BiasLayer = mlpack::ann::BiasLayer<>;
    using BaseLayer = mlpack::ann::BaseLayer<PerformanceFunction>;
    using ModulesType = std::tuple<LinearLayer, BiasLayer, BaseLayer,
        LinearLayer, BiasLayer, BaseLayer>;
    using FFNType = mlpack::ann::FFN<ModulesType, OutputLayerType,
        mlpack::ann::RandomInitialization, PerformanceFunctionType>;
};

template <typename PerformanceFunction,
         typename OutputLayerType,
         typename PerformanceFunctionType,
         typename MatType = arma::mat,
         typename Helper = BuildVanillaNetworkHelper<
             PerformanceFunction, OutputLayerType,
             PerformanceFunctionType, MatType>
         >
typename Helper::FFNType BuildVanillaNetwork(...);

【讨论】:

    猜你喜欢
    • 2014-05-28
    • 2013-03-29
    • 1970-01-01
    • 2018-02-21
    • 1970-01-01
    • 2016-04-19
    • 2013-04-26
    • 2014-11-27
    • 2013-02-18
    相关资源
    最近更新 更多