在 C++ 中使用自定义随机引擎包装器时出现 std::variant 编译错误

问题描述 投票:0回答:1

我正在开发一个 C++ 项目,在该项目中我实现了一个

RandomEngineWrapper
类,以使用
std::variant
封装各种随机数引擎。但是,当我尝试将此包装器与
std::visit
结合使用时遇到编译错误。

no matching function for call to 'std::variant<std::linear_congruential_engine<unsigned int, 16807, 0, 2147483647>, std::linear_congruential_engine<unsigned int, 16807, 0, 2147483647>, std::linear_congruential_engine<unsigned int, 48271, 0, 2147483647>, std::mersenne_twister_engine<unsigned int, 32, 624, 397, 31, 2567483615, 11, 4294967295, 7, 2636928640, 15, 4022730752, 18, 1812433253>, std::mersenne_twister_engine<long long unsigned int, 64, 312, 156, 31, 13043109905998158313, 29, 6148914691236517205, 17, 8202884508482404352, 37, 18444473444759240704, 43, 6364136223846793005>, std::subtract_with_carry_engine<unsigned int, 24, 10, 24>, std::subtract_with_carry_engine<long long unsigned int, 48, 5, 12>, std::discard_block_engine<std::subtract_with_carry_engine<unsigned int, 24, 10, 24>, 223, 23>, std::discard_block_engine<std::subtract_with_carry_engine<long long unsigned int, 48, 5, 12>, 389, 11>, std::shuffle_order_engine<std::linear_congruential_engine<unsigned int, 16807, 0, 2147483647>, 256> >::variant(std::linear_congruential_engine<unsigned int, 16807, 0, 2147483647>)'


no matching function for call to 'visit(generateRandomIndex<std::vector<int> >(const std::vector<int>&, ENGINE, DISTRIBUTION)::<lambda(auto:27&)>, std::linear_congruential_engine<unsigned int, 16807, 0, 2147483647>&)'

#include <random>
#include <variant>
#include <vector>
#include <iostream>

enum class ENGINE {
    DEFAULT_RANDOM_ENGINE,
    MINSTD_RAND0,
    MINSTD_RAND,
    MT19937,
    MT19937_64,
    RANLUX24_BASE,
    RANLUX48_BASE,
    RANLUX24,
    RANLUX48,
    KNUTH_B,
    RANDOM_DEVICE
};

enum class DISTRIBUTION {
    UNIFORM_INT,
};

std::random_device rd;

class RandomEngineWrapper {
private:
    std::variant<
        std::default_random_engine,
        std::minstd_rand0,
        std::minstd_rand,
        std::mt19937,
        std::mt19937_64,
        std::ranlux24_base,
        std::ranlux48_base,
        std::ranlux24,
        std::ranlux48,
        std::knuth_b
    > engine_variant;

public:
    template <typename T>
    RandomEngineWrapper(T&& rng) : engine_variant(std::forward<T>(rng)) {}

    template <typename T>
    T& get() {
        return std::get<T>(engine_variant);
    }
};

RandomEngineWrapper getEngine(ENGINE engine) {
    switch (engine) {
        case ENGINE::DEFAULT_RANDOM_ENGINE:
            return RandomEngineWrapper(std::default_random_engine(rd()));
        case ENGINE::MINSTD_RAND0:
            return RandomEngineWrapper(std::minstd_rand0(rd()));
        case ENGINE::MINSTD_RAND:
            return RandomEngineWrapper(std::minstd_rand(rd()));
        case ENGINE::MT19937:
            return RandomEngineWrapper(std::mt19937(rd()));
        case ENGINE::MT19937_64:
            return RandomEngineWrapper(std::mt19937_64(rd()));
        case ENGINE::RANLUX24_BASE:
            return RandomEngineWrapper(std::ranlux24_base(rd()));
        case ENGINE::RANLUX48_BASE:
            return RandomEngineWrapper(std::ranlux48_base(rd()));
        case ENGINE::RANLUX24:
            return RandomEngineWrapper(std::ranlux24(rd()));
        case ENGINE::RANLUX48:
            return RandomEngineWrapper(std::ranlux48(rd()));
        case ENGINE::KNUTH_B:
            return RandomEngineWrapper(std::knuth_b(rd()));
        case ENGINE::RANDOM_DEVICE:
        default:
            return RandomEngineWrapper(std::mt19937(rd())); // Default case
    }
}

auto getDistribution(DISTRIBUTION distribution, int size) {
    switch (distribution) {
        case DISTRIBUTION::UNIFORM_INT:
            return std::uniform_int_distribution<>(0, size - 1);
        default:
            return std::uniform_int_distribution<>(0, size - 1); // Default case
    }
}

template <typename Container>
int generateRandomIndex(const Container& container, ENGINE engineChoice, DISTRIBUTION distributionChoice) {
    auto engineWrapper = getEngine(engineChoice);
    auto dist = getDistribution(distributionChoice, container.size());
    
    int index = 0;
    std::visit([&](auto& engine) {
        index = dist(engine);
    }, engineWrapper.get<std::default_random_engine>());

    return index;
}

int main() {
    std::vector<int> myVector = {1, 2, 3, 4, 5};
    int index = generateRandomIndex(myVector, ENGINE::MT19937, DISTRIBUTION::UNIFORM_INT);
    std::cout << "Random index: " << index << std::endl;
    return 0;
}

std::visit
函数中的
generateRandomIndex
调用会产生错误:

no matching function for call to 'visit(generateRandomIndex<std::vector<int> >(const std::vector<int>&, ENGINE, DISTRIBUTION)::<lambda(auto:27&)>, std::linear_congruential_engine<unsigned int, 16807, 0, 2147483647>&)'

此外,在尝试使用各种随机引擎初始化

std::variant
时,我收到与
RandomEngineWrapper
构造函数相关的错误。 为什么我会收到这些错误,以及如何正确使用 std::variant 和 std::visit 来实现包装各种随机引擎并使用它们生成随机索引的目标?

###附加信息:###

  • 我使用
    std::variant
    来包装不同类型的随机数引擎。
  • RandomEngineWrapper
    类旨在抽象不同随机引擎的细节。
  • 我需要使用指定的随机引擎和分布在容器中生成随机索引。 任何有关解决这些错误的见解将不胜感激。
c++ template-meta-programming
1个回答
0
投票

关于

std::variant
构造函数的错误是由于变体中有两份
std::linear_congruential_engine<unsigned int, 16807, 0, 2147483647>
造成的,一份来自
std::default_random_engine
,另一份来自
std::minstd_rand0
。从变体中删除
std::default_random_engine
即可修复该问题。

std::visit()
调用中的错误是因为它期望一个变体作为第二个参数,而不是实际的引擎类型。这可以通过向
RandomEngineWrapper
添加一个调用
std::visit
的成员函数来解决(基于 C++26 中的
std::variant::visit()
):

template <typename Visitor>
decltype(auto) RandomEngineWrapper::useEngine(Visitor&& vis) {
    return std::visit(std::forward<Visitor>(vis), engine_variant);
}

可以在

generateRandomIndex()
中使用,如下所示:

int index;
engineWrapper.useEngine([&](auto& engine) {
    index = dist(engine);
});
© www.soinside.com 2019 - 2024. All rights reserved.