仅允许特定命名空间成员作为非类型模板参数的概念。

huangapple go评论70阅读模式
英文:

Concept that ONLY allows non-type template parameters that are members of certain namespace

问题

我有一个命名空间 Bar 和一个在 Bar 中声明的命名空间 Bar::Inner

然后,我有一个可变参数函数模板 foo。这个可变参数模板接受通用类型 T 的非类型参数。

然而,我希望这些非类型参数必须是命名空间 Bar 的成员,但不是命名空间 Bar::Inner 的成员。

并且,我希望使用概念来实现这一点,如果可能的话。

因此,总结一下,我基本上需要一个概念,只有当非类型模板参数是命名空间 Bar 的成员,但不是命名空间 Bar::Inner 的成员时才能接受。

英文:

I have a namespace Bar and a namespace Bar::Inner (declared within Bar).

// Bar
namespace Bar {
    size_t num = 32;
    
    // Bar::Inner
    namespace Inner {
        size_t num = 4;
    };
};

<br>

Then, I have a variadic function template foo. This variadic template accepts non-type parameters of common type T.
<br>
However, I want the non-type parameters to HAVE TO be members of namespace Bar, but NOT members of namespace Bar::Inner.

template&lt;typename T, T ...Sz&gt;
    requires MyConcept&lt;T, Sz...&gt;
auto foo()
{
    return (Sz + ...);
}

And I'd like to accomplish that using concepts, if possible.

template&lt;typename T, T ...Sz&gt;
concept MyConcept = (/*Whatever goes here*/);

So, to sum it up, I basically need a concept that can allow the non-type template parameters only if they are members of namespace Bar, but NOT of namespace Bar::Inner.

答案1

得分: 4

以下是您要翻译的代码部分:

// 可以通过命名空间过滤变量,如果允许使用一些扩展。
// 还必须知道您将不再接受值。这可能是一个重大不足。

// 首先,让我们声明`fixed_string`,这对于编译时字符串操作会很有用:

template<std::size_t n>
struct fixed_string {
    constexpr fixed_string() = default;
    constexpr fixed_string(const char(&str)[n + 1]) noexcept {
        auto i = std::size_t{0};
        for (char const c : str) {
            _data[i++] = c;
        }
    }

    friend constexpr auto operator<=>(fixed_string const&, fixed_string const&) = default;

    [[nodiscard]]
    static constexpr auto size() noexcept -> std::size_t {
        return n;
    }

    constexpr auto data() const& noexcept {
        return _data;
    }

    constexpr auto data() & noexcept {
        return _data;
    }

    constexpr auto begin() const& noexcept -> char const* {
        return _data;
    }

    constexpr auto end() const& noexcept -> char const* {
        return _data + n;
    }

    constexpr auto begin() & noexcept -> char* {
        return _data;
    }

    constexpr auto end() & noexcept -> char* {
        return _data + n;
    }

    constexpr auto operator[](std::size_t index) noexcept {
        return _data[index];
    }

    constexpr auto starts_with(std::string_view str) {
        return std::string_view{_data, n}.starts_with(str);
    }

    char _data[n + 1];
};

template<std::size_t n>
fixed_string(char const(&)[n]) -> fixed_string<n - 1>;

// 现在我们有了基本部分,让我们创建一个函数,使用编译器扩展返回对象的限定名称。
// 从技术上讲,如果使用特定于编译器的扩展并动态筛选字符串以仅返回对象的名称,
// 则可以相当可移植地实现。

template<auto& v>
constexpr auto name_of() -> std::string_view {
    constexpr auto size = std::string_view{__PRETTY_FUNCTION__}.size();
    constexpr auto drop_begin = "constexpr std::string_view name_of() [with auto& v = "sv.size();
    constexpr auto drop_end = "; std::string_view = std::basic_string_view<char>]sv.size();
    return std::string_view{__PRETTY_FUNCTION__}.substr(drop_begin, size - drop_end - drop_begin);
}

// 然后,我们可以创建一个概念,仅在限定名称以特定字符串开头时返回true:

template<auto& v, fixed_string namesp>
concept in_namespace = name_of<v>().starts_with(std::string_view{namesp.data(), namesp.size()});

// 现在,您可以编写一个仅接受特定命名空间中的对象的函数:

template<auto& v> requires in_namespace<v, "bar">
void im_clever();

// 要过滤掉内部命名空间,您可以这样做:

template<auto& v>
concept in_bar_not_in_inner = in_namespace<v, "bar"> and not in_namespace<v, "bar::inner">;

请注意,由于代码部分包含特定的C++代码和标识符,因此某些部分可能无法直接翻译或需要按原样保留。

英文:

It's entirely possible to filter variables by namespace if you allow yourself to use some extensions.

You have to also know that you won't accept values anymore. This may be a big downside.

First, let's declare fixed_string, this is gonna be useful for compile time string manipulation:

template&lt;std::size_t n&gt;
struct fixed_string {
constexpr fixed_string() = default;
constexpr fixed_string(const char(&amp;str)[n + 1]) noexcept {
auto i = std::size_t{0};
for (char const c : str) {
_data[i++] = c;
}
}
friend constexpr auto operator&lt;=&gt;(fixed_string const&amp;, fixed_string const&amp;) = default;
[[nodiscard]]
static constexpr auto size() noexcept -&gt; std::size_t {
return n;
}
constexpr auto data() const&amp; noexcept {
return _data;
}
constexpr auto data() &amp; noexcept {
return _data;
}
constexpr auto begin() const&amp; noexcept -&gt; char const* {
return _data;
}
constexpr auto end() const&amp; noexcept -&gt; char const* {
return _data + n;
}
constexpr auto begin() &amp; noexcept -&gt; char* {
return _data;
}
constexpr auto end() &amp; noexcept -&gt; char* {
return _data + n;
}
constexpr auto operator[](std::size_t index) noexcept {
return _data[index];
}
constexpr auto starts_with(std::string_view str) {
return std::string_view{_data, n}.starts_with(str);
}
char _data[n + 1];
};
template&lt;std::size_t n&gt;
fixed_string(char const(&amp;)[n]) -&gt; fixed_string&lt;n - 1&gt;;

Now that we have the basic, let's create a function that returns the qualified name of an object using a compiler extension. It's technically doable pretty portably if you use compiler specific extension and you dynamically filter the string so it returns only the name of the object.

template&lt;auto&amp; v&gt;
constexpr auto name_of() -&gt; std::string_view {
constexpr auto size = std::string_view{__PRETTY_FUNCTION__}.size();
constexpr auto drop_begin = &quot;constexpr std::string_view name_of() [with auto&amp; v = &quot;sv.size();
constexpr auto drop_end = &quot;; std::string_view = std::basic_string_view&lt;char&gt;]&quot;sv.size();
return std::string_view{__PRETTY_FUNCTION__}.substr(drop_begin, size - drop_end - drop_begin);
}

Then, we can create a concept to only yield true of the qualified name starts with a particular string:

template&lt;auto&amp; v, fixed_string namesp&gt;
concept in_namespace = name_of&lt;v&gt;().starts_with(std::string_view{namesp.data(), namesp.size()});

Now, you can write a function that only accepts objects from a particular namespace:

template&lt;auto&amp; v&gt; requires in_namespace&lt;v, &quot;bar&quot;&gt;
void im_clever();

To filter out the inner namespace you can do this:

template&lt;auto&amp; v&gt;
concept in_bar_not_in_inner = in_namespace&lt;v, &quot;bar&quot;&gt; and not in_namespace&lt;v, &quot;bar::inner&quot;&gt;;

See my code running in compiler explorer

答案2

得分: 0

非类型模板参数是值。这些值不属于命名空间;它们只是值。常量表达式 2 完全等同于任何求值为 2constexpr 值。没有区别。

如果你使用非类型模板参数,那么你是在说它可以假定用户提供的任何值。概念可以限制这种参数的实际值,但不能限制用于提供这些值的参数表达式。也就是说,当一个概念看到非类型模板参数时,关于它来自何处的所有信息都已丢失。

也就是说,如果某个模板 templ<2> 可以工作,那么只要常量表达式 expr 解析为值 2,它就可以替代 2

英文:

Non-type template parameters are... values. Values do not belong to a namespace; they're just values. The constant expression 2 is exactly equivalent to any constexpr value that evaluates to the value 2. There is no difference.

If you take an NTTP, you are saying that it can assume any value the user provides. A concept can restrict the actual values of such a parameter, but not the argument expression used to provide those values. That is, by the time a concept sees an NTTP, all information about where it came from has been lost.

That is, if some template templ&lt;2&gt; can work, any constant expression expr will work in place of 2 so long as it resolves to the value of 2.

huangapple
  • 本文由 发表于 2023年7月18日 00:38:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/76706506.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定